summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ftl/finalizer.h211
-rw-r--r--libs/binder/Parcel.cpp8
-rw-r--r--libs/binder/include/binder/Parcel.h9
-rw-r--r--libs/binder/tests/binderSafeInterfaceTest.cpp11
-rw-r--r--libs/binderdebug/stats.cpp6
-rw-r--r--libs/binderdebug/tests/binderdebug_test.cpp13
-rw-r--r--libs/ftl/Android.bp1
-rw-r--r--libs/ftl/finalizer_test.cpp209
-rw-r--r--libs/graphicsenv/GraphicsEnv.cpp8
-rw-r--r--libs/graphicsenv/include/graphicsenv/GraphicsEnv.h2
-rw-r--r--libs/gui/LayerState.cpp16
-rw-r--r--libs/gui/SurfaceComposerClient.cpp66
-rw-r--r--libs/gui/WindowInfo.cpp62
-rw-r--r--libs/gui/include/gui/LayerState.h14
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h14
-rw-r--r--libs/gui/include/gui/WindowInfo.h7
-rw-r--r--libs/gui/tests/WindowInfo_test.cpp16
-rw-r--r--libs/input/input_flags.aconfig10
-rw-r--r--libs/ui/include/ui/ShadowSettings.h4
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp88
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h8
-rw-r--r--services/inputflinger/dispatcher/InputTarget.h4
-rw-r--r--services/inputflinger/reader/Android.bp10
-rw-r--r--services/inputflinger/reader/mapper/TouchInputMapper.cpp12
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp50
-rw-r--r--services/inputflinger/tests/InputReader_test.cpp2
-rw-r--r--services/surfaceflinger/FrontEnd/LayerHierarchy.h2
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshot.cpp8
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshot.h15
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp48
-rw-r--r--services/surfaceflinger/FrontEnd/RequestedLayerState.cpp14
-rw-r--r--services/surfaceflinger/Layer.h5
-rw-r--r--services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp77
-rw-r--r--services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h23
-rw-r--r--services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp53
35 files changed, 928 insertions, 178 deletions
diff --git a/include/ftl/finalizer.h b/include/ftl/finalizer.h
new file mode 100644
index 0000000000..0251957ad0
--- /dev/null
+++ b/include/ftl/finalizer.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/function.h>
+
+namespace android::ftl {
+
+// An RAII wrapper that invokes a function object as a finalizer when destroyed.
+//
+// The function object must take no arguments, and must return void. If the function object needs
+// any context for the call, it must store it itself, for example with a lambda capture.
+//
+// The stored function object will be called once (unless canceled via the `cancel()` member
+// function) at the first of:
+//
+// - The Finalizer instance is destroyed.
+// - `operator()` is used to invoke the contained function.
+// - The Finalizer instance is move-assigned a new value. The function being replaced will be
+// invoked, and the replacement will be stored to be called later.
+//
+// The intent with this class is to keep cleanup code next to the code that requires that
+// cleanup be performed.
+//
+// bool read_file(std::string filename) {
+// FILE* f = fopen(filename.c_str(), "rb");
+// if (f == nullptr) return false;
+// const auto cleanup = ftl::Finalizer([f]() { fclose(f); });
+// // fread(...), etc
+// return true;
+// }
+//
+// The `FinalFunction` template argument to Finalizer<FinalFunction> allows a polymorphic function
+// type for storing the finalization function, such as `std::function` or `ftl::Function`.
+//
+// For convenience, this header defines a few useful aliases for using those types.
+//
+// - `FinalizerStd`, an alias for `Finalizer<std::function<void()>>`
+// - `FinalizerFtl`, an alias for `Finalizer<ftl::Function<void()>>`
+// - `FinalizerFtl1`, an alias for `Finalizer<ftl::Function<void(), 1>>`
+// - `FinalizerFtl2`, an alias for `Finalizer<ftl::Function<void(), 2>>`
+// - `FinalizerFtl3`, an alias for `Finalizer<ftl::Function<void(), 3>>`
+//
+// Clients of this header are free to define other aliases they need.
+//
+// A Finalizer that uses a polymorphic function type can be returned from a function call and/or
+// stored as member data (to be destroyed along with the containing class).
+//
+// auto register(Observer* observer) -> ftl::FinalizerStd<void()> {
+// const auto id = observers.add(observer);
+// return ftl::Finalizer([id]() { observers.remove(id); });
+// }
+//
+// {
+// const auto _ = register(observer);
+// // do the things that required the registered observer.
+// }
+// // the observer is removed.
+//
+// Cautions:
+//
+// 1. When a Finalizer is stored as member data, you will almost certainly want that cleanup to
+// happen first, before the rest of the other member data is destroyed. For safety you should
+// assume that the finalization function will access that data directly or indirectly.
+//
+// This means that Finalizers should be defined last, after all other normal member data in a
+// class.
+//
+// class MyClass {
+// public:
+// bool initialize() {
+// ready_ = true;
+// cleanup_ = ftl::Finalizer([this]() { ready_ = false; });
+// return true;
+// }
+//
+// bool ready_ = false;
+//
+// // Finalizers should be last so other class members can be accessed before being
+// // destroyed.
+// ftl::FinalizerStd<void()> cleanup_;
+// };
+//
+// 2. Care must be taken to use `ftl::Finalizer()` when constructing locally from a lambda. If you
+// forget to do so, you are just creating a lambda that won't be automatically invoked!
+//
+// const auto bad = [&counter](){ ++counter; }; // Just a lambda instance
+// const auto good = ftl::Finalizer([&counter](){ ++counter; });
+//
+template <typename FinalFunction>
+class Finalizer final {
+ // requires(std::is_invocable_r_v<void, FinalFunction>)
+ static_assert(std::is_invocable_r_v<void, FinalFunction>);
+
+ public:
+ // A default constructed Finalizer does nothing when destroyed.
+ // requires(std::is_default_constructible_v<FinalFunction>)
+ constexpr Finalizer() = default;
+
+ // Constructs a Finalizer from a function object.
+ // requires(std::is_invocable_v<F>)
+ template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
+ [[nodiscard]] explicit constexpr Finalizer(F&& function)
+ : Finalizer(std::forward<F>(function), false) {}
+
+ constexpr ~Finalizer() { maybe_invoke(); }
+
+ // Disallow copying.
+ Finalizer(const Finalizer& that) = delete;
+ auto operator=(const Finalizer& that) = delete;
+
+ // Move construction
+ // requires(std::is_move_constructible_v<FinalFunction>)
+ [[nodiscard]] constexpr Finalizer(Finalizer&& that)
+ : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {}
+
+ // Implicit conversion move construction
+ // requires(!std::is_same_v<Finalizer, Finalizer<F>>)
+ template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>>
+ // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved)
+ [[nodiscard]] constexpr Finalizer(Finalizer<F>&& that)
+ : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {}
+
+ // Move assignment
+ // requires(std::is_move_assignable_v<FinalFunction>)
+ constexpr auto operator=(Finalizer&& that) -> Finalizer& {
+ maybe_invoke();
+
+ function_ = std::move(that.function_);
+ canceled_ = std::exchange(that.canceled_, true);
+
+ return *this;
+ }
+
+ // Implicit conversion move assignment
+ // requires(!std::is_same_v<Finalizer, Finalizer<F>>)
+ template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>>
+ // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
+ constexpr auto operator=(Finalizer<F>&& that) -> Finalizer& {
+ *this = Finalizer(std::move(that.function_), std::exchange(that.canceled_, true));
+ return *this;
+ }
+
+ // Cancels the final function, preventing it from being invoked.
+ constexpr void cancel() {
+ canceled_ = true;
+ maybe_nullify_function();
+ }
+
+ // Invokes the final function now, if not already invoked.
+ constexpr void operator()() { maybe_invoke(); }
+
+ private:
+ template <typename>
+ friend class Finalizer;
+
+ template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
+ [[nodiscard]] explicit constexpr Finalizer(F&& function, bool canceled)
+ : function_(std::forward<F>(function)), canceled_(canceled) {}
+
+ constexpr void maybe_invoke() {
+ if (!std::exchange(canceled_, true)) {
+ std::invoke(function_);
+ maybe_nullify_function();
+ }
+ }
+
+ constexpr void maybe_nullify_function() {
+ // Sets function_ to nullptr if that is supported for the backing type.
+ if constexpr (std::is_assignable_v<FinalFunction, nullptr_t>) {
+ function_ = nullptr;
+ }
+ }
+
+ FinalFunction function_;
+ bool canceled_ = true;
+};
+
+template <typename F>
+Finalizer(F&&) -> Finalizer<std::decay_t<F>>;
+
+// A standard alias for using `std::function` as the polymorphic function type.
+using FinalizerStd = Finalizer<std::function<void()>>;
+
+// Helpful aliases for using `ftl::Function` as the polymorphic function type.
+using FinalizerFtl = Finalizer<Function<void()>>;
+using FinalizerFtl1 = Finalizer<Function<void(), 1>>;
+using FinalizerFtl2 = Finalizer<Function<void(), 2>>;
+using FinalizerFtl3 = Finalizer<Function<void(), 3>>;
+
+} // namespace android::ftl \ No newline at end of file
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a0ae93f56f..c0ebee006b 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -3389,14 +3389,6 @@ void Parcel::scanForFds() const {
}
#ifdef BINDER_WITH_KERNEL_IPC
-size_t Parcel::getBlobAshmemSize() const
-{
- // This used to return the size of all blobs that were written to ashmem, now we're returning
- // the ashmem currently referenced by this Parcel, which should be equivalent.
- // TODO(b/202029388): Remove method once ABI can be changed.
- return getOpenAshmemSize();
-}
-
size_t Parcel::getOpenAshmemSize() const
{
auto* kernelFields = maybeKernelFields();
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 988f490e45..6c4c6fe573 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -1488,14 +1488,15 @@ public:
* Note: for historical reasons, this does not include ashmem memory which
* is referenced by this Parcel, but which this parcel doesn't own (e.g.
* writeFileDescriptor is called without 'takeOwnership' true).
+ *
+ * WARNING: you should not use this, but rather, unparcel, and inspect
+ * each FD independently. This counts ashmem size, but there may be
+ * other resources used for non-ashmem FDs, such as other types of
+ * shared memory, files, etc..
*/
LIBBINDER_EXPORTED size_t getOpenAshmemSize() const;
private:
- // TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference
- // this
- LIBBINDER_EXPORTED size_t getBlobAshmemSize() const;
-
// Needed so that we can save object metadata to the disk
friend class android::binder::debug::RecordedTransaction;
};
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 7d1556e7d2..45b2103637 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -858,7 +858,13 @@ TEST_F(SafeInterfaceTest, TestIncrementTwo) {
ASSERT_EQ(b + 1, bPlusOne);
}
-extern "C" int main(int argc, char **argv) {
+} // namespace tests
+} // namespace android
+
+int main(int argc, char** argv) {
+ using namespace android;
+ using namespace android::tests;
+
testing::InitGoogleTest(&argc, argv);
if (fork() == 0) {
@@ -875,6 +881,3 @@ extern "C" int main(int argc, char **argv) {
return RUN_ALL_TESTS();
}
-
-} // namespace tests
-} // namespace android
diff --git a/libs/binderdebug/stats.cpp b/libs/binderdebug/stats.cpp
index 9c26afaa97..972fbd5295 100644
--- a/libs/binderdebug/stats.cpp
+++ b/libs/binderdebug/stats.cpp
@@ -22,9 +22,9 @@
#include <inttypes.h>
-namespace android {
+int main() {
+ using namespace android;
-extern "C" int main() {
// ignore args - we only print csv
// we should use a csv library here for escaping, because
@@ -58,5 +58,3 @@ extern "C" int main() {
}
return 0;
}
-
-} // namespace android
diff --git a/libs/binderdebug/tests/binderdebug_test.cpp b/libs/binderdebug/tests/binderdebug_test.cpp
index ea799c06a2..ad2b581abc 100644
--- a/libs/binderdebug/tests/binderdebug_test.cpp
+++ b/libs/binderdebug/tests/binderdebug_test.cpp
@@ -60,8 +60,15 @@ TEST(BinderDebugTests, BinderThreads) {
EXPECT_GE(pidInfo.threadCount, 1);
}
-extern "C" {
+} // namespace test
+} // namespace binderdebug
+} // namespace android
+
int main(int argc, char** argv) {
+ using namespace android;
+ using namespace android::binderdebug;
+ using namespace android::binderdebug::test;
+
::testing::InitGoogleTest(&argc, argv);
// Create a child/client process to call into the main process so we can ensure
@@ -84,7 +91,3 @@ int main(int argc, char** argv) {
return RUN_ALL_TESTS();
}
-} // extern "C"
-} // namespace test
-} // namespace binderdebug
-} // namespace android
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 368f5e079c..08ce855c2b 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -21,6 +21,7 @@ cc_test {
"enum_test.cpp",
"expected_test.cpp",
"fake_guard_test.cpp",
+ "finalizer_test.cpp",
"flags_test.cpp",
"function_test.cpp",
"future_test.cpp",
diff --git a/libs/ftl/finalizer_test.cpp b/libs/ftl/finalizer_test.cpp
new file mode 100644
index 0000000000..4f5c2258db
--- /dev/null
+++ b/libs/ftl/finalizer_test.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2024 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 <memory>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/finalizer.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+
+namespace {
+
+struct Counter {
+ constexpr auto increment_fn() {
+ return [this] { ++value_; };
+ }
+
+ auto increment_finalizer() {
+ return ftl::Finalizer([this] { ++value_; });
+ }
+
+ [[nodiscard]] constexpr auto value() const -> int { return value_; }
+
+ private:
+ int value_ = 0;
+};
+
+struct CounterPair {
+ constexpr auto increment_first_fn() { return first.increment_fn(); }
+ constexpr auto increment_second_fn() { return second.increment_fn(); }
+ [[nodiscard]] constexpr auto values() const -> std::pair<int, int> {
+ return {first.value(), second.value()};
+ }
+
+ private:
+ Counter first;
+ Counter second;
+};
+
+} // namespace
+
+TEST(Finalizer, DefaultConstructionAndNoOpDestructionWhenPolymorphicType) {
+ ftl::FinalizerStd finalizer1;
+ ftl::FinalizerFtl finalizer2;
+ ftl::FinalizerFtl1 finalizer3;
+ ftl::FinalizerFtl2 finalizer4;
+ ftl::FinalizerFtl3 finalizer5;
+}
+
+TEST(Finalizer, InvokesTheFunctionOnDestruction) {
+ Counter counter;
+ {
+ const auto finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ }
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, InvocationCanBeCanceled) {
+ Counter counter;
+ {
+ auto finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ finalizer.cancel();
+ EXPECT_EQ(counter.value(), 0);
+ }
+ EXPECT_EQ(counter.value(), 0);
+}
+
+TEST(Finalizer, InvokesTheFunctionOnce) {
+ Counter counter;
+ {
+ auto finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ finalizer();
+ EXPECT_EQ(counter.value(), 1);
+ finalizer();
+ EXPECT_EQ(counter.value(), 1);
+ }
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, SelfInvocationIsAllowedAndANoOp) {
+ Counter counter;
+ ftl::FinalizerStd finalizer;
+ finalizer = ftl::Finalizer([&]() {
+ counter.increment_fn()();
+ finalizer(); // recursive invocation should do nothing.
+ });
+ EXPECT_EQ(counter.value(), 0);
+ finalizer();
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, MoveConstruction) {
+ Counter counter;
+ {
+ ftl::FinalizerStd outer_finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ {
+ ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer);
+ static_assert(std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>);
+ EXPECT_EQ(counter.value(), 0);
+ }
+ EXPECT_EQ(counter.value(), 1);
+ }
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, MoveConstructionWithImplicitConversion) {
+ Counter counter;
+ {
+ auto outer_finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ {
+ ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer);
+ static_assert(!std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>);
+ EXPECT_EQ(counter.value(), 0);
+ }
+ EXPECT_EQ(counter.value(), 1);
+ }
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, MoveAssignment) {
+ CounterPair pair;
+ {
+ ftl::FinalizerStd outer_finalizer = ftl::Finalizer(pair.increment_first_fn());
+ EXPECT_EQ(pair.values(), std::make_pair(0, 0));
+
+ {
+ ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn());
+ static_assert(std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>);
+ EXPECT_EQ(pair.values(), std::make_pair(0, 0));
+ inner_finalizer = std::move(outer_finalizer);
+ EXPECT_EQ(pair.values(), std::make_pair(0, 1));
+ }
+ EXPECT_EQ(pair.values(), std::make_pair(1, 1));
+ }
+ EXPECT_EQ(pair.values(), std::make_pair(1, 1));
+}
+
+TEST(Finalizer, MoveAssignmentWithImplicitConversion) {
+ CounterPair pair;
+ {
+ auto outer_finalizer = ftl::Finalizer(pair.increment_first_fn());
+ EXPECT_EQ(pair.values(), std::make_pair(0, 0));
+
+ {
+ ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn());
+ static_assert(!std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>);
+ EXPECT_EQ(pair.values(), std::make_pair(0, 0));
+ inner_finalizer = std::move(outer_finalizer);
+ EXPECT_EQ(pair.values(), std::make_pair(0, 1));
+ }
+ EXPECT_EQ(pair.values(), std::make_pair(1, 1));
+ }
+ EXPECT_EQ(pair.values(), std::make_pair(1, 1));
+}
+
+TEST(Finalizer, NullifiesTheFunctionWhenInvokedIfPossible) {
+ auto shared = std::make_shared<int>(0);
+ std::weak_ptr<int> weak = shared;
+
+ int count = 0;
+ {
+ auto lambda = [capture = std::move(shared)]() {};
+ auto finalizer = ftl::Finalizer(std::move(lambda));
+ EXPECT_FALSE(weak.expired());
+
+ // A lambda is not nullable. Invoking the finalizer cannot destroy it to destroy the lambda's
+ // capture.
+ finalizer();
+ EXPECT_FALSE(weak.expired());
+ }
+ // The lambda is only destroyed when the finalizer instance is destroyed.
+ EXPECT_TRUE(weak.expired());
+
+ shared = std::make_shared<int>(0);
+ weak = shared;
+
+ {
+ auto lambda = [capture = std::move(shared)]() {};
+ auto finalizer = ftl::FinalizerStd(std::move(lambda));
+ EXPECT_FALSE(weak.expired());
+
+ // Since std::function is used, and is nullable, invoking the finalizer will destroy the
+ // contained function, which will destroy the lambda's capture.
+ finalizer();
+ EXPECT_TRUE(weak.expired());
+ }
+}
+
+} // namespace android::test
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index a8d5fe7371..4874dbde9c 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -596,7 +596,7 @@ bool GraphicsEnv::shouldUseAngle() {
// If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
const std::string& packageName,
- const std::vector<std::string> eglFeatures) {
+ const std::vector<std::string>& eglFeatures) {
if (mShouldUseAngle) {
// ANGLE is already set up for this application process, even if the application
// needs to switch from apk to system or vice versa, the application process must
@@ -606,11 +606,11 @@ void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNati
return;
}
- mAngleEglFeatures = std::move(eglFeatures);
+ mAngleEglFeatures = eglFeatures;
ALOGV("setting ANGLE path to '%s'", path.c_str());
- mAnglePath = std::move(path);
+ mAnglePath = path;
ALOGV("setting app package name to '%s'", packageName.c_str());
- mPackageName = std::move(packageName);
+ mPackageName = packageName;
if (mAnglePath == "system") {
mShouldUseSystemAngle = true;
}
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index b0ab0b9d22..452e48bb75 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -114,7 +114,7 @@ public:
// If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process.
// If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
void setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
- const std::string& packageName, const std::vector<std::string> eglFeatures);
+ const std::string& packageName, const std::vector<std::string>& eglFeatures);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
// Get the app package name.
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index c1a03fcfea..44aac9bfae 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -66,6 +66,8 @@ layer_state_t::layer_state_t()
mask(0),
reserved(0),
cornerRadius(0.0f),
+ clientDrawnCornerRadius(0.0f),
+ clientDrawnShadowRadius(0.0f),
backgroundBlurRadius(0),
color(0),
bufferTransform(0),
@@ -140,6 +142,8 @@ status_t layer_state_t::write(Parcel& output) const
SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float));
SAFE_PARCEL(output.writeFloat, cornerRadius);
+ SAFE_PARCEL(output.writeFloat, clientDrawnCornerRadius);
+ SAFE_PARCEL(output.writeFloat, clientDrawnShadowRadius);
SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
SAFE_PARCEL(output.writeParcelable, metadata);
SAFE_PARCEL(output.writeFloat, bgColor.r);
@@ -274,6 +278,8 @@ status_t layer_state_t::read(const Parcel& input)
SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float));
SAFE_PARCEL(input.readFloat, &cornerRadius);
+ SAFE_PARCEL(input.readFloat, &clientDrawnCornerRadius);
+ SAFE_PARCEL(input.readFloat, &clientDrawnShadowRadius);
SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
SAFE_PARCEL(input.readParcelable, &metadata);
@@ -596,6 +602,14 @@ void layer_state_t::merge(const layer_state_t& other) {
what |= eCornerRadiusChanged;
cornerRadius = other.cornerRadius;
}
+ if (other.what & eClientDrawnCornerRadiusChanged) {
+ what |= eClientDrawnCornerRadiusChanged;
+ clientDrawnCornerRadius = other.clientDrawnCornerRadius;
+ }
+ if (other.what & eClientDrawnShadowsChanged) {
+ what |= eClientDrawnShadowsChanged;
+ clientDrawnShadowRadius = other.clientDrawnShadowRadius;
+ }
if (other.what & eBackgroundBlurRadiusChanged) {
what |= eBackgroundBlurRadiusChanged;
backgroundBlurRadius = other.backgroundBlurRadius;
@@ -809,6 +823,8 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const {
}
CHECK_DIFF(diff, eLayerStackChanged, other, layerStack);
CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius);
+ CHECK_DIFF(diff, eClientDrawnCornerRadiusChanged, other, clientDrawnCornerRadius);
+ CHECK_DIFF(diff, eClientDrawnShadowsChanged, other, clientDrawnShadowRadius);
CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius);
if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged;
if (other.what & eRelativeLayerChanged) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index cabde22c6d..5bb8f7f8f7 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -829,9 +829,7 @@ SurfaceComposerClient::Transaction::Transaction() {
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
: mId(other.mId),
- mAnimation(other.mAnimation),
- mEarlyWakeupStart(other.mEarlyWakeupStart),
- mEarlyWakeupEnd(other.mEarlyWakeupEnd),
+ mFlags(other.mFlags),
mMayContainBuffer(other.mMayContainBuffer),
mDesiredPresentTime(other.mDesiredPresentTime),
mIsAutoTimestamp(other.mIsAutoTimestamp),
@@ -868,9 +866,7 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) {
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint64_t transactionId = parcel->readUint64();
- const bool animation = parcel->readBool();
- const bool earlyWakeupStart = parcel->readBool();
- const bool earlyWakeupEnd = parcel->readBool();
+ const uint32_t flags = parcel->readUint32();
const int64_t desiredPresentTime = parcel->readInt64();
const bool isAutoTimestamp = parcel->readBool();
const bool logCallPoints = parcel->readBool();
@@ -965,9 +961,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel
// Parsing was successful. Update the object.
mId = transactionId;
- mAnimation = animation;
- mEarlyWakeupStart = earlyWakeupStart;
- mEarlyWakeupEnd = earlyWakeupEnd;
+ mFlags = flags;
mDesiredPresentTime = desiredPresentTime;
mIsAutoTimestamp = isAutoTimestamp;
mFrameTimelineInfo = frameTimelineInfo;
@@ -996,9 +990,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const
const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
parcel->writeUint64(mId);
- parcel->writeBool(mAnimation);
- parcel->writeBool(mEarlyWakeupStart);
- parcel->writeBool(mEarlyWakeupEnd);
+ parcel->writeUint32(mFlags);
parcel->writeInt64(mDesiredPresentTime);
parcel->writeBool(mIsAutoTimestamp);
parcel->writeBool(mLogCallPoints);
@@ -1131,8 +1123,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr
mInputWindowCommands.merge(other.mInputWindowCommands);
mMayContainBuffer |= other.mMayContainBuffer;
- mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart;
- mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd;
+ mFlags |= other.mFlags;
mApplyToken = other.mApplyToken;
mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
@@ -1154,15 +1145,13 @@ void SurfaceComposerClient::Transaction::clear() {
mInputWindowCommands.clear();
mUncacheBuffers.clear();
mMayContainBuffer = false;
- mAnimation = false;
- mEarlyWakeupStart = false;
- mEarlyWakeupEnd = false;
mDesiredPresentTime = 0;
mIsAutoTimestamp = true;
mFrameTimelineInfo = {};
mApplyToken = nullptr;
mMergedTransactionIds.clear();
mLogCallPoints = false;
+ mFlags = 0;
}
uint64_t SurfaceComposerClient::Transaction::getId() {
@@ -1333,9 +1322,6 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
displayStates = std::move(mDisplayStates);
- if (mAnimation) {
- flags |= ISurfaceComposer::eAnimation;
- }
if (oneWay) {
if (synchronous) {
ALOGE("Transaction attempted to set synchronous and one way at the same time"
@@ -1345,15 +1331,12 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
}
}
- // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
+ // If both ISurfaceComposer::eEarlyWakeupStart and ISurfaceComposer::eEarlyWakeupEnd are set
// it is equivalent for none
- if (mEarlyWakeupStart && !mEarlyWakeupEnd) {
- flags |= ISurfaceComposer::eEarlyWakeupStart;
- }
- if (mEarlyWakeupEnd && !mEarlyWakeupStart) {
- flags |= ISurfaceComposer::eEarlyWakeupEnd;
+ uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd;
+ if ((flags & wakeupFlags) == wakeupFlags) {
+ flags &= ~(wakeupFlags);
}
-
sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
@@ -1461,15 +1444,15 @@ std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTran
}
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
- mAnimation = true;
+ mFlags |= ISurfaceComposer::eAnimation;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupStart() {
- mEarlyWakeupStart = true;
+ mFlags |= ISurfaceComposer::eEarlyWakeupStart;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() {
- mEarlyWakeupEnd = true;
+ mFlags |= ISurfaceComposer::eEarlyWakeupEnd;
}
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
@@ -1695,6 +1678,29 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCorne
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setClientDrawnCornerRadius(
+ const sp<SurfaceControl>& sc, float clientDrawnCornerRadius) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eClientDrawnCornerRadiusChanged;
+ s->clientDrawnCornerRadius = clientDrawnCornerRadius;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setClientDrawnShadowRadius(
+ const sp<SurfaceControl>& sc, float clientDrawnShadowRadius) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eClientDrawnShadowsChanged;
+ s->clientDrawnShadowRadius = clientDrawnShadowRadius;
+ return *this;
+}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundBlurRadius(
const sp<SurfaceControl>& sc, int backgroundBlurRadius) {
layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index e1a7ac9b18..3fb66d1f33 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -59,6 +59,32 @@ std::ostream& operator<<(std::ostream& out, const Region& region) {
return out;
}
+status_t writeTransform(android::Parcel* parcel, const ui::Transform& transform) {
+ return parcel->writeFloat(transform.dsdx()) ?:
+ parcel->writeFloat(transform.dtdx()) ?:
+ parcel->writeFloat(transform.tx()) ?:
+ parcel->writeFloat(transform.dtdy()) ?:
+ parcel->writeFloat(transform.dsdy()) ?:
+ parcel->writeFloat(transform.ty());
+}
+
+status_t readTransform(const android::Parcel* parcel, ui::Transform& transform) {
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+
+ const status_t status = parcel->readFloat(&dsdx) ?:
+ parcel->readFloat(&dtdx) ?:
+ parcel->readFloat(&tx) ?:
+ parcel->readFloat(&dtdy) ?:
+ parcel->readFloat(&dsdy) ?:
+ parcel->readFloat(&ty);
+ if (status != OK) {
+ return status;
+ }
+
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+ return OK;
+}
+
} // namespace
void WindowInfo::setInputConfig(ftl::Flags<InputConfig> config, bool value) {
@@ -131,12 +157,7 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const {
parcel->writeInt32(surfaceInset) ?:
parcel->writeFloat(globalScaleFactor) ?:
parcel->writeFloat(alpha) ?:
- parcel->writeFloat(transform.dsdx()) ?:
- parcel->writeFloat(transform.dtdx()) ?:
- parcel->writeFloat(transform.tx()) ?:
- parcel->writeFloat(transform.dtdy()) ?:
- parcel->writeFloat(transform.dsdy()) ?:
- parcel->writeFloat(transform.ty()) ?:
+ writeTransform(parcel, transform) ?:
parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
parcel->writeInt32(ownerPid.val()) ?:
parcel->writeInt32(ownerUid.val()) ?:
@@ -149,8 +170,12 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const {
parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?:
parcel->writeStrongBinder(windowToken) ?:
parcel->writeStrongBinder(focusTransferTarget) ?:
- parcel->writeBool(canOccludePresentation);
+ parcel->writeBool(canOccludePresentation) ?:
+ parcel->writeBool(cloneLayerStackTransform.has_value());
// clang-format on
+ if (cloneLayerStackTransform) {
+ status = status ?: writeTransform(parcel, *cloneLayerStackTransform);
+ }
return status;
}
@@ -170,10 +195,10 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
return status;
}
- float dsdx, dtdx, tx, dtdy, dsdy, ty;
int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt,
displayIdInt;
sp<IBinder> touchableRegionCropHandleSp;
+ bool hasCloneLayerStackTransform = false;
// clang-format off
status = parcel->readInt32(&lpFlags) ?:
@@ -184,12 +209,7 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
parcel->readInt32(&surfaceInset) ?:
parcel->readFloat(&globalScaleFactor) ?:
parcel->readFloat(&alpha) ?:
- parcel->readFloat(&dsdx) ?:
- parcel->readFloat(&dtdx) ?:
- parcel->readFloat(&tx) ?:
- parcel->readFloat(&dtdy) ?:
- parcel->readFloat(&dsdy) ?:
- parcel->readFloat(&ty) ?:
+ readTransform(parcel, /*byRef*/ transform) ?:
parcel->readInt32(&touchOcclusionModeInt) ?:
parcel->readInt32(&ownerPidInt) ?:
parcel->readInt32(&ownerUidInt) ?:
@@ -202,8 +222,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?:
parcel->readNullableStrongBinder(&windowToken) ?:
parcel->readNullableStrongBinder(&focusTransferTarget) ?:
- parcel->readBool(&canOccludePresentation);
-
+ parcel->readBool(&canOccludePresentation)?:
+ parcel->readBool(&hasCloneLayerStackTransform);
// clang-format on
if (status != OK) {
@@ -212,7 +232,6 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
layoutParamsFlags = ftl::Flags<Flag>(lpFlags);
layoutParamsType = static_cast<Type>(lpType);
- transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
inputConfig = ftl::Flags<InputConfig>(inputConfigInt);
ownerPid = Pid{ownerPidInt};
@@ -220,6 +239,15 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
touchableRegionCropHandle = touchableRegionCropHandleSp;
displayId = ui::LogicalDisplayId{displayIdInt};
+ cloneLayerStackTransform =
+ hasCloneLayerStackTransform ? std::make_optional<ui::Transform>() : std::nullopt;
+ if (cloneLayerStackTransform) {
+ status = readTransform(parcel, /*byRef*/ *cloneLayerStackTransform);
+ if (status != OK) {
+ return status;
+ }
+ }
+
return OK;
}
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 64f191b867..16425c9872 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -231,6 +231,8 @@ struct layer_state_t {
eBufferReleaseChannelChanged = 0x40000'00000000,
ePictureProfileHandleChanged = 0x80000'00000000,
eAppContentPriorityChanged = 0x100000'00000000,
+ eClientDrawnCornerRadiusChanged = 0x200000'00000000,
+ eClientDrawnShadowsChanged = 0x400000'00000000,
};
layer_state_t();
@@ -251,9 +253,9 @@ struct layer_state_t {
// Geometry updates.
static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged |
layer_state_t::eBufferTransformChanged | layer_state_t::eCornerRadiusChanged |
- layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged |
- layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged |
- layer_state_t::eTransformToDisplayInverseChanged |
+ layer_state_t::eClientDrawnCornerRadiusChanged | layer_state_t::eCropChanged |
+ layer_state_t::eDestinationFrameChanged | layer_state_t::eMatrixChanged |
+ layer_state_t::ePositionChanged | layer_state_t::eTransformToDisplayInverseChanged |
layer_state_t::eTransparentRegionChanged | layer_state_t::eEdgeExtensionChanged;
// Buffer and related updates.
@@ -274,8 +276,8 @@ struct layer_state_t {
layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged |
layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged |
- layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged |
- layer_state_t::eAppContentPriorityChanged;
+ layer_state_t::eClientDrawnShadowsChanged | layer_state_t::eStretchChanged |
+ layer_state_t::ePictureProfileHandleChanged | layer_state_t::eAppContentPriorityChanged;
// Changes which invalidates the layer's visible region in CE.
static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -328,6 +330,8 @@ struct layer_state_t {
uint8_t reserved;
matrix22_t matrix;
float cornerRadius;
+ float clientDrawnCornerRadius;
+ float clientDrawnShadowRadius;
uint32_t backgroundBlurRadius;
sp<SurfaceControl> relativeLayerSurfaceControl;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0f66c8b492..10c51a3e20 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -467,10 +467,7 @@ public:
std::vector<uint64_t> mMergedTransactionIds;
uint64_t mId;
-
- bool mAnimation = false;
- bool mEarlyWakeupStart = false;
- bool mEarlyWakeupEnd = false;
+ uint32_t mFlags;
// Indicates that the Transaction may contain buffers that should be cached. The reason this
// is only a guess is that buffers can be removed before cache is called. This is only a
@@ -567,6 +564,15 @@ public:
Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setCrop(const sp<SurfaceControl>& sc, const FloatRect& crop);
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+ // Sets the client drawn corner radius for the layer. If both a corner radius and a client
+ // radius are sent to SF, the client radius will be used. This indicates that the corner
+ // radius is drawn by the client and not SurfaceFlinger.
+ Transaction& setClientDrawnCornerRadius(const sp<SurfaceControl>& sc,
+ float clientDrawnCornerRadius);
+ // Sets the client drawn shadow radius for the layer. This indicates that the shadows
+ // are drawn by the client and not SurfaceFlinger.
+ Transaction& setClientDrawnShadowRadius(const sp<SurfaceControl>& sc,
+ float clientDrawnShadowRadius);
Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
int backgroundBlurRadius);
Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index f7e6084eef..420dc2103f 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -218,9 +218,14 @@ struct WindowInfo : public Parcelable {
// An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
float alpha;
- // Transform applied to individual windows.
+ // Transform applied to individual windows for input.
+ // Maps display coordinates to the window's input coordinate space.
ui::Transform transform;
+ // Transform applied to get to the layer stack space of the cloned window for input.
+ // Maps display coordinates of the clone window to the layer stack space of the cloned window.
+ std::optional<ui::Transform> cloneLayerStackTransform;
+
/*
* This is filled in by the WM relative to the frame and then translated
* to absolute coordinates by SurfaceFlinger once the frame is computed.
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index ce22082a9f..e3f9a07b34 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -40,7 +40,18 @@ TEST(WindowInfo, ParcellingWithoutToken) {
ASSERT_EQ(OK, i.writeToParcel(&p));
p.setDataPosition(0);
i2.readFromParcel(&p);
- ASSERT_TRUE(i2.token == nullptr);
+ ASSERT_EQ(i2.token, nullptr);
+}
+
+TEST(WindowInfo, ParcellingWithoutCloneTransform) {
+ WindowInfo i, i2;
+ i.cloneLayerStackTransform.reset();
+
+ Parcel p;
+ ASSERT_EQ(OK, i.writeToParcel(&p));
+ p.setDataPosition(0);
+ i2.readFromParcel(&p);
+ ASSERT_EQ(i2.cloneLayerStackTransform, std::nullopt);
}
TEST(WindowInfo, Parcelling) {
@@ -71,6 +82,8 @@ TEST(WindowInfo, Parcelling) {
i.applicationInfo.token = new BBinder();
i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD;
i.focusTransferTarget = new BBinder();
+ i.cloneLayerStackTransform = ui::Transform();
+ i.cloneLayerStackTransform->set({5, -1, 100, 4, 0, 40, 0, 0, 1});
Parcel p;
i.writeToParcel(&p);
@@ -100,6 +113,7 @@ TEST(WindowInfo, Parcelling) {
ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
ASSERT_EQ(i.applicationInfo, i2.applicationInfo);
ASSERT_EQ(i.focusTransferTarget, i2.focusTransferTarget);
+ ASSERT_EQ(i.cloneLayerStackTransform, i2.cloneLayerStackTransform);
}
TEST(InputApplicationInfo, Parcelling) {
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index feff096720..09042c2099 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -224,3 +224,13 @@ flag {
description: "Allow cursor to transition across multiple connected displays"
bug: "362719483"
}
+
+flag {
+ name: "use_cloned_screen_coordinates_as_raw"
+ namespace: "input"
+ description: "Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones"
+ bug: "377846505"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/ui/include/ui/ShadowSettings.h b/libs/ui/include/ui/ShadowSettings.h
index c0b83b8691..06be6dbbf5 100644
--- a/libs/ui/include/ui/ShadowSettings.h
+++ b/libs/ui/include/ui/ShadowSettings.h
@@ -46,6 +46,9 @@ struct ShadowSettings {
// Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
float length = 0.f;
+ // Length of the cast shadow that is drawn by the client.
+ float clientDrawnLength = 0.f;
+
// If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
// Otherwise the shadow will only be drawn around the edges of the casting layer.
bool casterIsTranslucent = false;
@@ -55,6 +58,7 @@ static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& r
return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
+ lhs.clientDrawnLength == rhs.clientDrawnLength &&
lhs.casterIsTranslucent == rhs.casterIsTranslucent;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ee31f1e308..15043db7fb 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -418,7 +418,7 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerato
if (inputTarget.useDefaultPointerTransform() && !zeroCoords) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
- inputTarget.displayTransform,
+ inputTarget.rawTransform,
inputTarget.globalScaleFactor, uid, vsyncId,
windowId);
}
@@ -439,7 +439,7 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerato
transform =
&inputTarget.getTransformForPointer(firstMarkedBit(inputTarget.getPointerIds()));
const ui::Transform inverseTransform = transform->inverse();
- displayTransform = &inputTarget.displayTransform;
+ displayTransform = &inputTarget.rawTransform;
// Iterate through all pointers in the event to normalize against the first.
for (size_t i = 0; i < motionEntry.getPointerCount(); i++) {
@@ -905,7 +905,7 @@ InputTarget createInputTarget(const std::shared_ptr<Connection>& connection,
const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags,
- const ui::Transform& displayTransform,
+ const ui::Transform& rawTransform,
std::optional<nsecs_t> firstDownTimeInTarget) {
LOG_ALWAYS_FATAL_IF(connection == nullptr);
InputTarget inputTarget{connection};
@@ -913,7 +913,7 @@ InputTarget createInputTarget(const std::shared_ptr<Connection>& connection,
inputTarget.dispatchMode = dispatchMode;
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor;
- inputTarget.displayTransform = displayTransform;
+ inputTarget.rawTransform = rawTransform;
inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
return inputTarget;
}
@@ -974,7 +974,7 @@ InputDispatcher::~InputDispatcher() {
while (!mConnectionsByToken.empty()) {
std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second;
- removeInputChannelLocked(connection->getToken(), /*notify=*/false);
+ removeInputChannelLocked(connection, /*notify=*/false);
}
}
@@ -2947,11 +2947,10 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa
windowHandle->getName().c_str());
return;
}
- inputTargets.push_back(createInputTarget(connection, windowHandle, dispatchMode,
- targetFlags,
- mWindowInfos.getDisplayTransform(
- windowHandle->getInfo()->displayId),
- firstDownTimeInTarget));
+ inputTargets.push_back(
+ createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
+ mWindowInfos.getRawTransform(*windowHandle->getInfo()),
+ firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -3002,11 +3001,10 @@ void InputDispatcher::addPointerWindowTargetLocked(
windowHandle->getName().c_str());
return;
}
- inputTargets.push_back(createInputTarget(connection, windowHandle, dispatchMode,
- targetFlags,
- mWindowInfos.getDisplayTransform(
- windowHandle->getInfo()->displayId),
- firstDownTimeInTarget));
+ inputTargets.push_back(
+ createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
+ mWindowInfos.getRawTransform(*windowHandle->getInfo()),
+ firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -3038,9 +3036,10 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>&
for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) {
InputTarget target{monitor.connection};
// target.firstDownTimeInTarget is not set for global monitors. It is only required in split
- // touch and global monitoring works as intended even without setting firstDownTimeInTarget
- target.displayTransform = mWindowInfos.getDisplayTransform(displayId);
- target.setDefaultPointerTransform(target.displayTransform);
+ // touch and global monitoring works as intended even without setting firstDownTimeInTarget.
+ // Since global monitors don't have windows, use the display transform as the raw transform.
+ target.rawTransform = mWindowInfos.getDisplayTransform(displayId);
+ target.setDefaultPointerTransform(target.rawTransform);
inputTargets.push_back(target);
}
}
@@ -3864,7 +3863,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
"event to it, status=%s(%d)",
connection->getInputChannelName().c_str(), statusToString(status).c_str(),
status);
- abortBrokenDispatchCycleLocked(currentTime, connection, /*notify=*/true);
+ abortBrokenDispatchCycleLocked(connection, /*notify=*/true);
} else {
// Pipe is full and we are waiting for the app to finish process some events
// before sending more events to it.
@@ -3879,7 +3878,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
"status=%s(%d)",
connection->getInputChannelName().c_str(), statusToString(status).c_str(),
status);
- abortBrokenDispatchCycleLocked(currentTime, connection, /*notify=*/true);
+ abortBrokenDispatchCycleLocked(connection, /*notify=*/true);
}
return;
}
@@ -3954,8 +3953,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
postCommandLocked(std::move(command));
}
-void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
- const std::shared_ptr<Connection>& connection,
+void InputDispatcher::abortBrokenDispatchCycleLocked(const std::shared_ptr<Connection>& connection,
bool notify) {
if (DEBUG_DISPATCH_CYCLE) {
LOG(INFO) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
@@ -4071,7 +4069,7 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok
}
// Remove the channel.
- removeInputChannelLocked(connection->getToken(), notify);
+ removeInputChannelLocked(connection, notify);
return 0; // remove the callback
}
@@ -4225,9 +4223,10 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
motionEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
+ // Since we don't have a window, use the display transform as the raw transform.
const ui::Transform displayTransform =
mWindowInfos.getDisplayTransform(motionEntry.displayId);
- targets.back().displayTransform = displayTransform;
+ targets.back().rawTransform = displayTransform;
targets.back().setDefaultPointerTransform(displayTransform);
}
logOutboundMotionDetails("cancel - ", motionEntry);
@@ -4310,9 +4309,10 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
targets);
} else {
targets.emplace_back(connection, targetFlags);
+ // Since we don't have a window, use the display transform as the raw transform.
const ui::Transform displayTransform =
mWindowInfos.getDisplayTransform(motionEntry.displayId);
- targets.back().displayTransform = displayTransform;
+ targets.back().rawTransform = displayTransform;
targets.back().setDefaultPointerTransform(displayTransform);
}
logOutboundMotionDetails("down - ", motionEntry);
@@ -5223,6 +5223,16 @@ ui::Transform InputDispatcher::DispatcherWindowInfo::getDisplayTransform(
: kIdentityTransform;
}
+ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
+ const android::gui::WindowInfo& windowInfo) const {
+ // If the window has a cloneLayerStackTransform, always use it as the transform for the "getRaw"
+ // APIs. If not, fall back to using the DisplayInfo transform of the window's display.
+ return (input_flags::use_cloned_screen_coordinates_as_raw() &&
+ windowInfo.cloneLayerStackTransform)
+ ? *windowInfo.cloneLayerStackTransform
+ : getDisplayTransform(windowInfo.displayId);
+}
+
std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const {
std::string dump;
if (!mWindowHandlesByDisplay.empty()) {
@@ -6224,8 +6234,14 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
{ // acquire lock
std::scoped_lock _l(mLock);
+ std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ // Connection can be removed via socket hang up or an explicit call to
+ // 'removeInputChannel'
+ return BAD_VALUE;
+ }
- status_t status = removeInputChannelLocked(connectionToken, /*notify=*/false);
+ status_t status = removeInputChannelLocked(connection, /*notify=*/false);
if (status) {
return status;
}
@@ -6237,25 +6253,18 @@ status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken)
return OK;
}
-status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
+status_t InputDispatcher::removeInputChannelLocked(const std::shared_ptr<Connection>& connection,
bool notify) {
- std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
- if (connection == nullptr) {
- // Connection can be removed via socket hang up or an explicit call to 'removeInputChannel'
- return BAD_VALUE;
- }
-
+ LOG_ALWAYS_FATAL_IF(connection == nullptr);
+ abortBrokenDispatchCycleLocked(connection, notify);
removeConnectionLocked(connection);
if (connection->monitor) {
- removeMonitorChannelLocked(connectionToken);
+ removeMonitorChannelLocked(connection->getToken());
}
mLooper->removeFd(connection->inputPublisher.getChannel().getFd());
- nsecs_t currentTime = now();
- abortBrokenDispatchCycleLocked(currentTime, connection, notify);
-
connection->status = Connection::Status::ZOMBIE;
return OK;
}
@@ -6489,9 +6498,8 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
createInputTarget(connection, windowHandle,
InputTarget::DispatchMode::AS_IS,
dispatchEntry->targetFlags,
- mWindowInfos.getDisplayTransform(
- windowHandle->getInfo()
- ->displayId),
+ mWindowInfos.getRawTransform(
+ *windowHandle->getInfo()),
downTime));
}
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index a88e4a3787..bca1c67a43 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -384,6 +384,9 @@ private:
// Get the transform for display, returns Identity-transform if display is missing.
ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const;
+ // Get the raw transform to use for motion events going to the given window.
+ ui::Transform getRawTransform(const android::gui::WindowInfo&) const;
+
// Lookup for WindowInfoHandle from token and optionally a display-id. In cases where
// display-id is not provided lookup is done for all displays.
sp<android::gui::WindowInfoHandle> findWindowHandle(
@@ -646,8 +649,7 @@ private:
void finishDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection, uint32_t seq,
bool handled, nsecs_t consumeTime) REQUIRES(mLock);
- void abortBrokenDispatchCycleLocked(nsecs_t currentTime,
- const std::shared_ptr<Connection>& connection, bool notify)
+ void abortBrokenDispatchCycleLocked(const std::shared_ptr<Connection>& connection, bool notify)
REQUIRES(mLock);
void drainDispatchQueue(std::deque<std::unique_ptr<DispatchEntry>>& queue);
void releaseDispatchEntry(std::unique_ptr<DispatchEntry> dispatchEntry);
@@ -693,7 +695,7 @@ private:
// Registration.
void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
- status_t removeInputChannelLocked(const sp<IBinder>& connectionToken, bool notify)
+ status_t removeInputChannelLocked(const std::shared_ptr<Connection>& connection, bool notify)
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 90374f1481..76f3fe0d0c 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -77,8 +77,8 @@ public:
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
- // Current display transform. Used for compatibility for raw coordinates.
- ui::Transform displayTransform;
+ // The raw coordinate transform that's used for compatibility for MotionEvent's getRaw APIs.
+ ui::Transform rawTransform;
// Event time for the first motion event (ACTION_DOWN) dispatched to this input target if
// FLAG_SPLIT is set.
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index b3cd35c936..3934e783d1 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -79,25 +79,25 @@ cc_defaults {
srcs: [":libinputreader_sources"],
shared_libs: [
"android.companion.virtualdevice.flags-aconfig-cc",
+ "libPlatformProperties",
"libbase",
"libcap",
"libcrypto",
"libcutils",
- "libjsoncpp",
"libinput",
+ "libjsoncpp",
"liblog",
- "libPlatformProperties",
"libstatslog",
"libstatspull",
- "libutils",
"libstatssocket",
+ "libutils",
],
static_libs: [
"libchrome-gestures",
- "libui-types",
"libexpresslog",
- "libtextclassifier_hash_static",
"libstatslog_express",
+ "libtextclassifier_hash_static",
+ "libui-types",
],
header_libs: [
"libbatteryservice_headers",
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 5c90cbb6ce..d9e7054322 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -3476,7 +3476,7 @@ std::list<NotifyArgs> TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs
}
return dispatchPointerSimple(when, readTime, policyFlags, down, hovering,
- ui::LogicalDisplayId::INVALID);
+ getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID));
}
std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime,
@@ -3967,14 +3967,8 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask,
}
std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() {
- if (mParameters.hasAssociatedDisplay) {
- if (mDeviceMode == DeviceMode::POINTER) {
- return ui::LogicalDisplayId::INVALID;
- } else {
- return std::make_optional(mViewport.displayId);
- }
- }
- return std::nullopt;
+ return mParameters.hasAssociatedDisplay ? std::make_optional(mViewport.displayId)
+ : std::nullopt;
}
} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c160ca6aa8..6ec08892c0 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -6478,19 +6478,47 @@ TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinate
ui::LogicalDisplayId::DEFAULT, {PointF{150, 220}}));
firstWindow->assertNoEvents();
- std::unique_ptr<MotionEvent> event = secondWindow->consumeMotionEvent();
- ASSERT_NE(nullptr, event);
- EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
+ secondWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ // Ensure that the events from the "getRaw" API are in logical display
+ // coordinates, which has an x-scale of 2 and y-scale of 4.
+ WithRawCoords(300, 880),
+ // Ensure that the x and y values are in the window's coordinate space.
+ // The left-top of the second window is at (100, 200) in display space, which is
+ // (200, 800) in the logical display space. This will be the origin of the window
+ // space.
+ WithCoords(100, 80)));
+}
- // Ensure that the events from the "getRaw" API are in logical display coordinates.
- EXPECT_EQ(300, event->getRawX(0));
- EXPECT_EQ(880, event->getRawY(0));
+TEST_F(InputDispatcherDisplayProjectionTest, UseCloneLayerStackTransformForRawCoordinates) {
+ SCOPED_FLAG_OVERRIDE(use_cloned_screen_coordinates_as_raw, true);
- // Ensure that the x and y values are in the window's coordinate space.
- // The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
- // the logical display space. This will be the origin of the window space.
- EXPECT_EQ(100, event->getX(0));
- EXPECT_EQ(80, event->getY(0));
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set(matrix);
+ addDisplayInfo(SECOND_DISPLAY_ID, secondDisplayTransform);
+
+ // When a clone layer stack transform is provided for a window, we should use that as the
+ // "display transform" for input going to that window.
+ sp<FakeWindowHandle> secondWindowClone = secondWindow->clone(SECOND_DISPLAY_ID);
+ secondWindowClone->editInfo()->cloneLayerStackTransform = ui::Transform();
+ secondWindowClone->editInfo()->cloneLayerStackTransform->set(0.5, 0, 0, 0.25);
+ addWindow(secondWindowClone);
+
+ // Touch down on the clone window, and ensure its raw coordinates use
+ // the clone layer stack transform.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ SECOND_DISPLAY_ID, {PointF{150, 220}}));
+ secondWindowClone->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ // Ensure the x and y coordinates are in the window's coordinate space.
+ // See previous test case for calculation.
+ WithCoords(100, 80),
+ // Ensure the "getRaw" API uses the clone layer stack transform when it is
+ // provided for the window. It has an x-scale of 0.5 and y-scale of 0.25.
+ WithRawCoords(75, 55)));
}
TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 0906536e32..2daa1e9f49 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -7534,7 +7534,7 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(ui::LogicalDisplayId::INVALID, motionArgs.displayId);
+ ASSERT_EQ(DISPLAY_ID, motionArgs.displayId);
}
/**
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 47d0041a8b..4fdbae1831 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -102,6 +102,8 @@ public:
// Returns true if the node is a clone.
bool isClone() const { return !mirrorRootIds.empty(); }
+ TraversalPath getClonedFrom() const { return {.id = id, .variant = variant}; }
+
bool operator==(const TraversalPath& other) const {
return id == other.id && mirrorRootIds == other.mirrorRootIds;
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 367132c113..42f3202f20 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -398,9 +398,13 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate
if (forceUpdate || requested.what & layer_state_t::eSidebandStreamChanged) {
sidebandStream = requested.sidebandStream;
}
- if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) {
- shadowSettings.length = requested.shadowRadius;
+ if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged ||
+ requested.what & layer_state_t::eClientDrawnShadowsChanged) {
+ shadowSettings.length =
+ requested.clientDrawnShadowRadius > 0 ? 0.f : requested.shadowRadius;
+ shadowSettings.clientDrawnLength = requested.clientDrawnShadowRadius;
}
+
if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) {
frameRateSelectionPriority = requested.frameRateSelectionPriority;
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index b8df3ed748..68b13959a8 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -28,16 +28,23 @@ namespace android::surfaceflinger::frontend {
struct RoundedCornerState {
RoundedCornerState() = default;
- RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
- : cropRect(cropRect), radius(radius) {}
// Rounded rectangle in local layer coordinate space.
FloatRect cropRect = FloatRect();
- // Radius of the rounded rectangle.
+ // Radius of the rounded rectangle for composition
vec2 radius;
+ // Requested radius of the rounded rectangle
+ vec2 requestedRadius;
+ // Radius drawn by client for the rounded rectangle
+ vec2 clientDrawnRadius;
+ bool hasClientDrawnRadius() const {
+ return clientDrawnRadius.x > 0.0f && clientDrawnRadius.y > 0.0f;
+ }
+ bool hasRequestedRadius() const { return requestedRadius.x > 0.0f && requestedRadius.y > 0.0f; }
bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
bool operator==(RoundedCornerState const& rhs) const {
- return cropRect == rhs.cropRect && radius == rhs.radius;
+ return cropRect == rhs.cropRect && radius == rhs.radius &&
+ clientDrawnRadius == rhs.clientDrawnRadius;
}
};
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 022588deef..0be3a1153d 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -16,6 +16,8 @@
// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include "FrontEnd/LayerSnapshot.h"
+#include "ui/Transform.h"
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
@@ -25,6 +27,7 @@
#include <common/FlagManager.h>
#include <common/trace.h>
#include <ftl/small_map.h>
+#include <math/vec2.h>
#include <ui/DisplayMap.h>
#include <ui/FloatRect.h>
@@ -932,7 +935,8 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a
if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
snapshot.changes.any(RequestedLayerState::Changes::Geometry |
- RequestedLayerState::Changes::BufferUsageFlags)) {
+ RequestedLayerState::Changes::BufferUsageFlags) ||
+ snapshot.clientChanges & layer_state_t::eClientDrawnCornerRadiusChanged) {
updateRoundedCorner(snapshot, requested, parentSnapshot, args);
}
@@ -972,7 +976,7 @@ void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot,
}
snapshot.roundedCorner = RoundedCornerState();
RoundedCornerState parentRoundedCorner;
- if (parentSnapshot.roundedCorner.hasRoundedCorners()) {
+ if (parentSnapshot.roundedCorner.hasRequestedRadius()) {
parentRoundedCorner = parentSnapshot.roundedCorner;
ui::Transform t = snapshot.localTransform.inverse();
parentRoundedCorner.cropRect = t.transform(parentRoundedCorner.cropRect);
@@ -981,10 +985,16 @@ void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot,
}
FloatRect layerCropRect = snapshot.croppedBufferSize;
- const vec2 radius(requested.cornerRadius, requested.cornerRadius);
- RoundedCornerState layerSettings(layerCropRect, radius);
- const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty();
- const bool parentRoundedCornerValid = parentRoundedCorner.hasRoundedCorners();
+ const vec2 requestedRadius(requested.cornerRadius, requested.cornerRadius);
+ const vec2 clientDrawnRadius(requested.clientDrawnCornerRadius,
+ requested.clientDrawnCornerRadius);
+ RoundedCornerState layerSettings;
+ layerSettings.cropRect = layerCropRect;
+ layerSettings.requestedRadius = requestedRadius;
+ layerSettings.clientDrawnRadius = clientDrawnRadius;
+
+ const bool layerSettingsValid = layerSettings.hasRequestedRadius() && !layerCropRect.isEmpty();
+ const bool parentRoundedCornerValid = parentRoundedCorner.hasRequestedRadius();
if (layerSettingsValid && parentRoundedCornerValid) {
// If the parent and the layer have rounded corner settings, use the parent settings if
// the parent crop is entirely inside the layer crop. This has limitations and cause
@@ -1002,6 +1012,14 @@ void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot,
} else if (parentRoundedCornerValid) {
snapshot.roundedCorner = parentRoundedCorner;
}
+
+ if (snapshot.roundedCorner.requestedRadius.x == requested.clientDrawnCornerRadius) {
+ // If the client drawn radius matches the requested radius, then surfaceflinger
+ // does not need to draw rounded corners for this layer
+ snapshot.roundedCorner.radius = vec2(0.f, 0.f);
+ } else {
+ snapshot.roundedCorner.radius = snapshot.roundedCorner.requestedRadius;
+ }
}
/**
@@ -1205,13 +1223,27 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot,
snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(),
snapshot.croppedBufferSize.getWidth()};
- // If the layer is a clone, we need to crop the input region to cloned root to prevent
- // touches from going outside the cloned area.
+ snapshot.inputInfo.cloneLayerStackTransform.reset();
+
if (path.isClone()) {
snapshot.inputInfo.inputConfig |= InputConfig::CLONE;
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
snapshot.inputInfo.inputConfig.clear(InputConfig::WATCH_OUTSIDE_TOUCH);
+
+ // Compute the transform that maps the clone's display to the layer stack space of the
+ // cloned window.
+ const LayerSnapshot* clonedSnapshot = getSnapshot(path.getClonedFrom());
+ if (clonedSnapshot != nullptr) {
+ const auto& [clonedInputBounds, s] =
+ getInputBounds(*clonedSnapshot, /*fillParentBounds=*/false);
+ ui::Transform inputToLayer;
+ inputToLayer.set(clonedInputBounds.left, clonedInputBounds.top);
+ const ui::Transform& layerToLayerStack = getInputTransform(*clonedSnapshot);
+ const auto& displayToInput = snapshot.inputInfo.transform;
+ snapshot.inputInfo.cloneLayerStackTransform =
+ layerToLayerStack * inputToLayer * displayToInput;
+ }
}
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index ee9302b937..591ebb2043 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -107,6 +107,8 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args)
hdrMetadata.validTypes = 0;
surfaceDamageRegion = Region::INVALID_REGION;
cornerRadius = 0.0f;
+ clientDrawnCornerRadius = 0.0f;
+ clientDrawnShadowRadius = 0.0f;
backgroundBlurRadius = 0;
api = -1;
hasColorTransform = false;
@@ -348,6 +350,16 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta
requestedFrameRate.category = category;
changes |= RequestedLayerState::Changes::FrameRate;
}
+
+ if (clientState.what & layer_state_t::eClientDrawnCornerRadiusChanged) {
+ clientDrawnCornerRadius = clientState.clientDrawnCornerRadius;
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
+
+ if (clientState.what & layer_state_t::eClientDrawnShadowsChanged) {
+ clientDrawnShadowRadius = clientState.clientDrawnShadowRadius;
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
}
ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const {
@@ -624,6 +636,8 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const {
const uint64_t deniedChanges = layer_state_t::ePositionChanged | layer_state_t::eAlphaChanged |
layer_state_t::eColorTransformChanged | layer_state_t::eBackgroundColorChanged |
layer_state_t::eMatrixChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eClientDrawnCornerRadiusChanged |
+ layer_state_t::eClientDrawnShadowsChanged |
layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBufferTransformChanged |
layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged |
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c234a75693..6af0f59d51 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -516,11 +516,6 @@ private:
bool mGetHandleCalled = false;
- // The inherited shadow radius after taking into account the layer hierarchy. This is the
- // final shadow radius for this layer. If a shadow is specified for a layer, then effective
- // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
- float mEffectiveShadowRadius = 0.f;
-
// Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats.
gui::GameMode mGameMode = gui::GameMode::Unsupported;
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index f247c9f088..151611cad2 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -585,6 +585,83 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, ParentCornerRadiusTakesPrecedence)
}
}
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetClientDrawnCornerRadius) {
+ sp<SurfaceControl> layer;
+ const uint8_t size = 64;
+ const uint8_t testArea = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
+
+ Transaction()
+ .setClientDrawnCornerRadius(layer, cornerRadius)
+ .setCornerRadius(layer, cornerRadius)
+ .setCrop(layer, Rect(size, size))
+ .apply();
+
+ {
+ const uint8_t bottom = size - 1;
+ const uint8_t right = size - 1;
+ auto shot = getScreenCapture();
+ // Solid corners
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::RED);
+ shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::RED);
+ shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::RED);
+ shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::RED);
+ // Solid center
+ shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+ size / 2 + testArea / 2, size / 2 + testArea / 2),
+ Color::RED);
+ }
+}
+
+// Test if ParentCornerRadiusTakesPrecedence if the parent's client drawn corner radius crop
+// is fully contained by the child corner radius crop.
+TEST_P(LayerTypeAndRenderTypeTransactionTest, ParentCornerRadiusPrecedenceClientDrawnCornerRadius) {
+ sp<SurfaceControl> parent;
+ sp<SurfaceControl> child;
+ const uint32_t size = 64;
+ const uint32_t parentSize = size * 3;
+ const Rect parentCrop(size, size, size, size);
+ const uint32_t testLength = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .setCrop(parent, parentCrop)
+ .setClientDrawnCornerRadius(parent, cornerRadius)
+ .reparent(child, parent)
+ .setPosition(child, size, size)
+ .apply(true);
+
+ {
+ const uint32_t top = size;
+ const uint32_t left = size;
+ const uint32_t bottom = size * 2;
+ const uint32_t right = size * 2;
+ auto shot = getScreenCapture();
+ // Corners are RED because parent's client drawn corner radius is actually 0
+ // and the child is fully within the parent's crop
+ // TL
+ shot->expectColor(Rect(left, top, testLength, testLength), Color::RED);
+ // TR
+ shot->expectColor(Rect(right - testLength, top, testLength, testLength), Color::RED);
+ // BL
+ shot->expectColor(Rect(left, bottom - testLength, testLength, testLength), Color::RED);
+ // BR
+ shot->expectColor(Rect(right - testLength, bottom - testLength, testLength, testLength),
+ Color::RED);
+ // Solid center
+ shot->expectColor(Rect(parentSize / 2 - testLength, parentSize / 2 - testLength, testLength,
+ testLength),
+ Color::GREEN);
+ }
+}
+
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) {
if (!deviceSupportsBlurs()) GTEST_SKIP();
if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index a894c418f6..ee5d9193ea 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -485,6 +485,29 @@ public:
mLifecycleManager.applyTransactions(transactions);
}
+ void setClientDrawnCornerRadius(uint32_t id, float clientDrawnCornerRadius) {
+ std::vector<QueuedTransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what =
+ layer_state_t::eClientDrawnCornerRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.clientDrawnCornerRadius = clientDrawnCornerRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setClientDrawnShadowRadius(uint32_t id, float clientDrawnShadowRadius) {
+ std::vector<QueuedTransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eClientDrawnShadowsChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.clientDrawnShadowRadius = clientDrawnShadowRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setShadowRadius(uint32_t id, float shadowRadius) {
std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 1177d1601e..2deb177496 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -1425,6 +1425,59 @@ TEST_F(LayerSnapshotTest, setBufferCrop) {
EXPECT_EQ(getSnapshot(1)->geomContentCrop, Rect(0, 0, 100, 100));
}
+TEST_F(LayerSnapshotTest, setCornerRadius) {
+ static constexpr float RADIUS = 123.f;
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect{1000, 1000});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, RADIUS);
+}
+
+TEST_F(LayerSnapshotTest, ignoreCornerRadius) {
+ static constexpr float RADIUS = 123.f;
+ setClientDrawnCornerRadius(1, RADIUS);
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect{1000, 1000});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasClientDrawnRadius());
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 0.f);
+}
+
+TEST_F(LayerSnapshotTest, childInheritsParentIntendedCornerRadius) {
+ static constexpr float RADIUS = 123.f;
+
+ setClientDrawnCornerRadius(1, RADIUS);
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect(1, 1, 999, 999));
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasClientDrawnRadius());
+ EXPECT_TRUE(getSnapshot({.id = 11})->roundedCorner.hasRoundedCorners());
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, RADIUS);
+}
+
+TEST_F(LayerSnapshotTest, childIgnoreCornerRadiusOverridesParent) {
+ static constexpr float RADIUS = 123.f;
+
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect(1, 1, 999, 999));
+
+ setClientDrawnCornerRadius(11, RADIUS);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, RADIUS);
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, 0.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->roundedCorner.radius.x, RADIUS);
+}
+
+TEST_F(LayerSnapshotTest, ignoreShadows) {
+ static constexpr float SHADOW_RADIUS = 123.f;
+ setClientDrawnShadowRadius(1, SHADOW_RADIUS);
+ setShadowRadius(1, SHADOW_RADIUS);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->shadowSettings.length, 0.f);
+}
+
TEST_F(LayerSnapshotTest, setShadowRadius) {
static constexpr float SHADOW_RADIUS = 123.f;
setShadowRadius(1, SHADOW_RADIUS);