[automerger skipped] Undo rotation by physical orientation in WindowInfo transforms am: 9afe784f82 am: 330f824c65 -s ours

am skip reason: Merged-In I727b22c004014355f337da2b753b61c5946667d0 with SHA-1 31e76ed995 is already in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/15585232

Change-Id: Iece96c6951709b7cceaadf1271d4817bb3eb582f
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 783a475..9a8ec32 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -244,6 +244,7 @@
         { OPT,      "events/kmem/ion_heap_shrink/enable" },
         { OPT,      "events/ion/ion_stat/enable" },
         { OPT,      "events/gpu_mem/gpu_mem_total/enable" },
+        { OPT,      "events/fastrpc/fastrpc_dma_stat/enable" },
     } },
     { "thermal",  "Thermal event", 0, {
         { REQ,      "events/thermal/thermal_temperature/enable" },
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index cfd42fe..3f7c7d6 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -528,7 +528,7 @@
 void Replayer::setLayerStack(SurfaceComposerClient::Transaction& t,
         layer_id id, const LayerStackChange& lsc) {
     ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
-    t.setLayerStack(mLayers[id], lsc.layer_stack());
+    t.setLayerStack(mLayers[id], ui::LayerStack::fromValue(lsc.layer_stack()));
 }
 
 void Replayer::setHiddenFlag(SurfaceComposerClient::Transaction& t,
@@ -566,7 +566,7 @@
 
 void Replayer::setDisplayLayerStack(SurfaceComposerClient::Transaction& t,
         display_id id, const LayerStackChange& lsc) {
-    t.setDisplayLayerStack(mDisplays[id], lsc.layer_stack());
+    t.setDisplayLayerStack(mDisplays[id], ui::LayerStack::fromValue(lsc.layer_stack()));
 }
 
 void Replayer::setDisplaySize(SurfaceComposerClient::Transaction& t,
diff --git a/include/android/input.h b/include/android/input.h
index bb98beb..f03facb 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -169,6 +169,9 @@
 
     /** Drag event */
     AINPUT_EVENT_TYPE_DRAG = 5,
+
+    /** TouchMode event */
+    AINPUT_EVENT_TYPE_TOUCH_MODE = 6,
 };
 
 /**
diff --git a/include/ftl/cast.h b/include/ftl/cast.h
new file mode 100644
index 0000000..ff1b58a
--- /dev/null
+++ b/include/ftl/cast.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <limits>
+#include <type_traits>
+
+#include <ftl/details/cast.h>
+
+namespace android::ftl {
+
+enum class CastSafety { kSafe, kUnderflow, kOverflow };
+
+// Returns whether static_cast<R>(v) is safe, or would result in underflow or overflow.
+//
+//   static_assert(ftl::cast_safety<uint8_t>(-1) == ftl::CastSafety::kUnderflow);
+//   static_assert(ftl::cast_safety<int8_t>(128u) == ftl::CastSafety::kOverflow);
+//
+//   static_assert(ftl::cast_safety<uint32_t>(-.1f) == ftl::CastSafety::kUnderflow);
+//   static_assert(ftl::cast_safety<int32_t>(static_cast<float>(INT32_MAX)) ==
+//                                           ftl::CastSafety::kOverflow);
+//
+//   static_assert(ftl::cast_safety<float>(-DBL_MAX) == ftl::CastSafety::kUnderflow);
+//
+template <typename R, typename T>
+constexpr CastSafety cast_safety(T v) {
+  static_assert(std::is_arithmetic_v<T>);
+  static_assert(std::is_arithmetic_v<R>);
+
+  constexpr bool kFromSigned = std::is_signed_v<T>;
+  constexpr bool kToSigned = std::is_signed_v<R>;
+
+  using details::max_exponent;
+
+  // If the R range contains the T range, then casting is always safe.
+  if constexpr ((kFromSigned == kToSigned && max_exponent<R> >= max_exponent<T>) ||
+                (!kFromSigned && kToSigned && max_exponent<R> > max_exponent<T>)) {
+    return CastSafety::kSafe;
+  }
+
+  using C = std::common_type_t<R, T>;
+
+  if constexpr (kFromSigned) {
+    using L = details::safe_limits<R, T>;
+
+    if constexpr (kToSigned) {
+      // Signed to signed.
+      if (v < L::lowest()) return CastSafety::kUnderflow;
+      return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow;
+    } else {
+      // Signed to unsigned.
+      if (v < 0) return CastSafety::kUnderflow;
+      return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe
+                                                           : CastSafety::kOverflow;
+    }
+  } else {
+    using L = std::numeric_limits<R>;
+
+    if constexpr (kToSigned) {
+      // Unsigned to signed.
+      return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe
+                                                           : CastSafety::kOverflow;
+    } else {
+      // Unsigned to unsigned.
+      return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow;
+    }
+  }
+}
+
+}  // namespace android::ftl
diff --git a/include/ftl/details/cast.h b/include/ftl/details/cast.h
new file mode 100644
index 0000000..87b9f1e
--- /dev/null
+++ b/include/ftl/details/cast.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <limits>
+#include <type_traits>
+
+namespace android::ftl::details {
+
+// Exponent whose power of 2 is the (exclusive) upper bound of T.
+template <typename T, typename L = std::numeric_limits<T>>
+constexpr int max_exponent = std::is_floating_point_v<T> ? L::max_exponent : L::digits;
+
+// Extension of std::numeric_limits<T> that reduces the maximum for integral types T such that it
+// has an exact representation for floating-point types F. For example, the maximum int32_t value
+// is 2'147'483'647, but casting it to float commonly rounds up to 2'147'483'650.f, which cannot
+// be safely converted back lest the signed overflow invokes undefined behavior. This pitfall is
+// avoided by clearing the lower (31 - 24 =) 7 bits of precision to 2'147'483'520. Note that the
+// minimum is representable.
+template <typename T, typename F>
+struct safe_limits : std::numeric_limits<T> {
+  static constexpr T max() {
+    using Base = std::numeric_limits<T>;
+
+    if constexpr (std::is_integral_v<T> && std::is_floating_point_v<F>) {
+      // Assume the mantissa is 24 bits for float, or 53 bits for double.
+      using Float = std::numeric_limits<F>;
+      static_assert(Float::is_iec559);
+
+      // If the integer is wider than the mantissa, clear the excess bits of precision.
+      constexpr int kShift = Base::digits - Float::digits;
+      if constexpr (kShift > 0) {
+        using U = std::make_unsigned_t<T>;
+        constexpr U kOne = static_cast<U>(1);
+        return static_cast<U>(Base::max()) & ~((kOne << kShift) - kOne);
+      }
+    }
+
+    return Base::max();
+  }
+};
+
+}  // namespace android::ftl::details
diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h
index 84c15eb..bcaba82 100644
--- a/include/ftl/small_map.h
+++ b/include/ftl/small_map.h
@@ -19,6 +19,7 @@
 #include <ftl/initializer_list.h>
 #include <ftl/small_vector.h>
 
+#include <algorithm>
 #include <functional>
 #include <optional>
 #include <type_traits>
@@ -28,7 +29,10 @@
 
 // Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are
 // stored in contiguous storage for cache efficiency. The map is allocated statically until its size
-// exceeds N, at which point mappings are relocated to dynamic memory.
+// exceeds N, at which point mappings are relocated to dynamic memory. The try_emplace operation has
+// a non-standard analogue try_replace that destructively emplaces. The API also defines an in-place
+// counterpart to insert_or_assign: emplace_or_replace. Lookup is done not via a subscript operator,
+// but immutable getters that can optionally transform the value.
 //
 // SmallMap<K, V, 0> unconditionally allocates on the heap.
 //
@@ -43,16 +47,19 @@
 //   assert(!map.dynamic());
 //
 //   assert(map.contains(123));
-//   assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u);
+//   assert(map.get(42, [](const std::string& s) { return s.size(); }) == 3u);
 //
-//   const auto opt = map.find(-1);
+//   const auto opt = map.get(-1);
 //   assert(opt);
 //
 //   std::string& ref = *opt;
 //   assert(ref.empty());
 //   ref = "xyz";
 //
-//   assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+//   map.emplace_or_replace(0, "vanilla", 2u, 3u);
+//   assert(map.dynamic());
+//
+//   assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
 //
 template <typename K, typename V, std::size_t N>
 class SmallMap final {
@@ -80,12 +87,7 @@
   // The syntax for listing pairs is as follows:
   //
   //   ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
-  //
   //   static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>);
-  //   assert(map.size() == 3u);
-  //   assert(map.contains(-1) && map.find(-1)->get().empty());
-  //   assert(map.contains(42) && map.find(42)->get() == "???");
-  //   assert(map.contains(123) && map.find(123)->get() == "abc");
   //
   // The types of the key and value are deduced if the first pair contains exactly two arguments:
   //
@@ -95,7 +97,7 @@
   template <typename U, std::size_t... Sizes, typename... Types>
   SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list)
       : map_(std::move(list)) {
-    // TODO: Enforce unique keys.
+    deduplicate();
   }
 
   size_type max_size() const { return map_.max_size(); }
@@ -115,27 +117,27 @@
 
   // Returns whether a mapping exists for the given key.
   bool contains(const key_type& key) const {
-    return find(key, [](const mapped_type&) {});
+    return get(key, [](const mapped_type&) {});
   }
 
   // Returns a reference to the value for the given key, or std::nullopt if the key was not found.
   //
   //   ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
   //
-  //   const auto opt = map.find('c');
+  //   const auto opt = map.get('c');
   //   assert(opt == 'C');
   //
   //   char d = 'd';
-  //   const auto ref = map.find('d').value_or(std::ref(d));
+  //   const auto ref = map.get('d').value_or(std::ref(d));
   //   ref.get() = 'D';
   //   assert(d == 'D');
   //
-  auto find(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> {
-    return find(key, [](const mapped_type& v) { return std::cref(v); });
+  auto get(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> {
+    return get(key, [](const mapped_type& v) { return std::cref(v); });
   }
 
-  auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
-    return find(key, [](mapped_type& v) { return std::ref(v); });
+  auto get(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
+    return get(key, [](mapped_type& v) { return std::ref(v); });
   }
 
   // Returns the result R of a unary operation F on (a constant or mutable reference to) the value
@@ -144,11 +146,11 @@
   //
   //   ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
   //
-  //   assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z');
-  //   assert(map.find('c', [](char& c) { c = std::toupper(c); }));
+  //   assert(map.get('c', [](char c) { return std::toupper(c); }) == 'Z');
+  //   assert(map.get('c', [](char& c) { c = std::toupper(c); }));
   //
   template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>>
-  auto find(const key_type& key, F f) const
+  auto get(const key_type& key, F f) const
       -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> {
     for (auto& [k, v] : *this) {
       if (k == key) {
@@ -165,12 +167,96 @@
   }
 
   template <typename F>
-  auto find(const key_type& key, F f) {
-    return std::as_const(*this).find(
+  auto get(const key_type& key, F f) {
+    return std::as_const(*this).get(
         key, [&f](const mapped_type& v) { return f(const_cast<mapped_type&>(v)); });
   }
 
+  // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise.
+  const_iterator find(const key_type& key) const { return const_cast<SmallMap&>(*this).find(key); }
+  iterator find(const key_type& key) { return find(key, begin()); }
+
+  // Inserts a mapping unless it exists. Returns an iterator to the inserted or existing mapping,
+  // and whether the mapping was inserted.
+  //
+  // On emplace, if the map reaches its static or dynamic capacity, then all iterators are
+  // invalidated. Otherwise, only the end() iterator is invalidated.
+  //
+  template <typename... Args>
+  std::pair<iterator, bool> try_emplace(const key_type& key, Args&&... args) {
+    if (const auto it = find(key); it != end()) {
+      return {it, false};
+    }
+
+    auto& ref = map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key),
+                                  std::forward_as_tuple(std::forward<Args>(args)...));
+    return {&ref, true};
+  }
+
+  // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator
+  // otherwise.
+  //
+  // The value is replaced via move constructor, so type V 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 mapping being replaced.
+  //
+  // Iterators to the replaced mapping point to its replacement, and others remain valid.
+  //
+  template <typename... Args>
+  iterator try_replace(const key_type& key, Args&&... args) {
+    const auto it = find(key);
+    if (it == end()) return it;
+    map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key),
+                 std::forward_as_tuple(std::forward<Args>(args)...));
+    return it;
+  }
+
+  // In-place counterpart of std::unordered_map's insert_or_assign. Returns true on emplace, or
+  // false on replace.
+  //
+  // The value is emplaced and replaced via move constructor, so type V does not need to define
+  // copy/move assignment, e.g. its data members may be const.
+  //
+  // On emplace, if the map reaches its static or dynamic capacity, then all iterators are
+  // invalidated. Otherwise, only the end() iterator is invalidated. On replace, iterators
+  // to the replaced mapping point to its replacement, and others remain valid.
+  //
+  template <typename... Args>
+  std::pair<iterator, bool> emplace_or_replace(const key_type& key, Args&&... args) {
+    const auto [it, ok] = try_emplace(key, std::forward<Args>(args)...);
+    if (ok) return {it, ok};
+    map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key),
+                 std::forward_as_tuple(std::forward<Args>(args)...));
+    return {it, ok};
+  }
+
+  // Removes a mapping if it exists, and returns whether it did.
+  //
+  // The last() and end() iterators, as well as those to the erased mapping, are invalidated.
+  //
+  bool erase(const key_type& key) { return erase(key, begin()); }
+
  private:
+  iterator find(const key_type& key, iterator first) {
+    return std::find_if(first, end(), [&key](const auto& pair) { return pair.first == key; });
+  }
+
+  bool erase(const key_type& key, iterator first) {
+    const auto it = find(key, first);
+    if (it == end()) return false;
+    map_.unstable_erase(it);
+    return true;
+  }
+
+  void deduplicate() {
+    for (auto it = begin(); it != end();) {
+      if (const auto key = it->first; ++it != end()) {
+        while (erase(key, it));
+      }
+    }
+  }
+
   Map map_;
 };
 
@@ -186,7 +272,7 @@
 
   for (const auto& [k, v] : lhs) {
     const auto& lv = v;
-    if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
+    if (!rhs.get(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
       return false;
     }
   }
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
index cb0ae35..0341435 100644
--- a/include/ftl/small_vector.h
+++ b/include/ftl/small_vector.h
@@ -348,7 +348,7 @@
   using Impl::pop_back;
 
   void unstable_erase(iterator it) {
-    if (it != last()) std::iter_swap(it, last());
+    if (it != last()) replace(it, std::move(back()));
     pop_back();
   }
 
diff --git a/include/ftl/string.h b/include/ftl/string.h
new file mode 100644
index 0000000..2d96b06
--- /dev/null
+++ b/include/ftl/string.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cassert>
+#include <charconv>
+#include <limits>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+namespace android::ftl {
+
+enum class Radix { kBin = 2, kDec = 10, kHex = 16 };
+
+template <typename T>
+struct to_chars_length {
+  static_assert(std::is_integral_v<T>);
+  // Maximum binary digits, plus minus sign and radix prefix.
+  static constexpr std::size_t value = std::numeric_limits<std::make_unsigned_t<T>>::digits + 3;
+};
+
+template <typename T>
+constexpr std::size_t to_chars_length_v = to_chars_length<T>::value;
+
+template <typename T = std::int64_t>
+using to_chars_buffer_t = char[to_chars_length_v<T>];
+
+// Lightweight (not allocating nor sprintf-based) alternative to std::to_string for integers, with
+// optional radix. See also ftl::to_string below.
+//
+//   ftl::to_chars_buffer_t<> buffer;
+//
+//   assert(ftl::to_chars(buffer, 123u) == "123");
+//   assert(ftl::to_chars(buffer, -42, ftl::Radix::kBin) == "-0b101010");
+//   assert(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex) == "0xcafe");
+//   assert(ftl::to_chars(buffer, '*', ftl::Radix::kHex) == "0x2a");
+//
+template <typename T, std::size_t N>
+std::string_view to_chars(char (&buffer)[N], T v, Radix radix = Radix::kDec) {
+  static_assert(N >= to_chars_length_v<T>);
+
+  auto begin = buffer + 2;
+  const auto [end, err] = std::to_chars(begin, buffer + N, v, static_cast<int>(radix));
+  assert(err == std::errc());
+
+  if (radix == Radix::kDec) {
+    // TODO: Replace with {begin, end} in C++20.
+    return {begin, static_cast<std::size_t>(end - begin)};
+  }
+
+  const auto prefix = radix == Radix::kBin ? 'b' : 'x';
+  if constexpr (std::is_unsigned_v<T>) {
+    buffer[0] = '0';
+    buffer[1] = prefix;
+  } else {
+    if (*begin == '-') {
+      *buffer = '-';
+    } else {
+      --begin;
+    }
+
+    *begin-- = prefix;
+    *begin = '0';
+  }
+
+  // TODO: Replace with {buffer, end} in C++20.
+  return {buffer, static_cast<std::size_t>(end - buffer)};
+}
+
+// Lightweight (not sprintf-based) alternative to std::to_string for integers, with optional radix.
+//
+//   assert(ftl::to_string(123u) == "123");
+//   assert(ftl::to_string(-42, ftl::Radix::kBin) == "-0b101010");
+//   assert(ftl::to_string(0xcafe, ftl::Radix::kHex) == "0xcafe");
+//   assert(ftl::to_string('*', ftl::Radix::kHex) == "0x2a");
+//
+template <typename T>
+inline std::string to_string(T v, Radix radix = Radix::kDec) {
+  to_chars_buffer_t<T> buffer;
+  return std::string(to_chars(buffer, v, radix));
+}
+
+std::string to_string(bool) = delete;
+std::string to_string(bool, Radix) = delete;
+
+}  // namespace android::ftl
diff --git a/include/input/Input.h b/include/input/Input.h
index cd110e8..2e42409 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -369,8 +369,6 @@
     float getAxisValue(int32_t axis) const;
     status_t setAxisValue(int32_t axis, float value);
 
-    void scale(float globalScale);
-
     // Scale the pointer coordinates according to a global scale and a
     // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
     // axes, however the window scaling will not.
@@ -888,6 +886,25 @@
     float mX, mY;
 };
 
+/*
+ * Touch mode events.
+ */
+class TouchModeEvent : public InputEvent {
+public:
+    virtual ~TouchModeEvent() {}
+
+    virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_TOUCH_MODE; }
+
+    inline bool isInTouchMode() const { return mIsInTouchMode; }
+
+    void initialize(int32_t id, bool isInTouchMode);
+
+    void initialize(const TouchModeEvent& from);
+
+protected:
+    bool mIsInTouchMode;
+};
+
 /**
  * Base class for verified events.
  * Do not create a VerifiedInputEvent explicitly.
@@ -952,6 +969,7 @@
     virtual FocusEvent* createFocusEvent() = 0;
     virtual CaptureEvent* createCaptureEvent() = 0;
     virtual DragEvent* createDragEvent() = 0;
+    virtual TouchModeEvent* createTouchModeEvent() = 0;
 };
 
 /*
@@ -968,6 +986,7 @@
     virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
     virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; }
     virtual DragEvent* createDragEvent() override { return &mDragEvent; }
+    virtual TouchModeEvent* createTouchModeEvent() override { return &mTouchModeEvent; }
 
 private:
     KeyEvent mKeyEvent;
@@ -975,6 +994,7 @@
     FocusEvent mFocusEvent;
     CaptureEvent mCaptureEvent;
     DragEvent mDragEvent;
+    TouchModeEvent mTouchModeEvent;
 };
 
 /*
@@ -990,6 +1010,7 @@
     virtual FocusEvent* createFocusEvent() override;
     virtual CaptureEvent* createCaptureEvent() override;
     virtual DragEvent* createDragEvent() override;
+    virtual TouchModeEvent* createTouchModeEvent() override;
 
     void recycle(InputEvent* event);
 
@@ -1001,6 +1022,7 @@
     std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
     std::queue<std::unique_ptr<CaptureEvent>> mCaptureEventPool;
     std::queue<std::unique_ptr<DragEvent>> mDragEventPool;
+    std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEventPool;
 };
 
 } // namespace android
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index a790b56..9a150eb 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -72,6 +72,7 @@
         CAPTURE,
         DRAG,
         TIMELINE,
+        TOUCH_MODE,
     };
 
     struct Header {
@@ -206,6 +207,15 @@
 
             inline size_t size() const { return sizeof(Timeline); }
         } timeline;
+
+        struct TouchMode {
+            int32_t eventId;
+            // The following 2 fields take up 4 bytes total
+            bool isInTouchMode;
+            uint8_t empty[3];
+
+            inline size_t size() const { return sizeof(TouchMode); }
+        } touchMode;
     } __attribute__((aligned(8))) body;
 
     bool isValid(size_t actualSize) const;
@@ -388,6 +398,15 @@
      */
     status_t publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting);
 
+    /* Publishes a touch mode event to the input channel.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if the channel is full.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode);
+
     struct Finished {
         uint32_t seq;
         bool handled;
@@ -658,6 +677,7 @@
     static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
     static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
     static void initializeDragEvent(DragEvent* event, const InputMessage* msg);
+    static void initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg);
     static void addSample(MotionEvent* event, const InputMessage* msg);
     static bool canAddSample(const Batch& batch, const InputMessage* msg);
     static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 2825273..20bf301 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -286,6 +286,9 @@
         "aidl/android/content/pm/IPackageChangeObserver.aidl",
         "aidl/android/content/pm/IPackageManagerNative.aidl",
         "aidl/android/content/pm/PackageChangeEvent.aidl",
+        "aidl/android/content/pm/IStagedApexObserver.aidl",
+        "aidl/android/content/pm/ApexStagedEvent.aidl",
+        "aidl/android/content/pm/StagedApexInfo.aidl",
     ],
     path: "aidl",
 }
diff --git a/libs/ui/Size.cpp b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
similarity index 62%
copy from libs/ui/Size.cpp
copy to libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
index d2996d1..75f8753 100644
--- a/libs/ui/Size.cpp
+++ b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
+package android.content.pm;
 
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+/**
+ * This event is designed for notification to native code listener about
+ * any changes to set of apex packages staged for installation on next boot.
+ *
+ * @hide
+ */
+parcelable ApexStagedEvent {
+  @utf8InCpp String[] stagedApexModuleNames;
+}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index c20d9f6..d71f496 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -18,6 +18,8 @@
 package android.content.pm;
 
 import android.content.pm.IPackageChangeObserver;
+import android.content.pm.IStagedApexObserver;
+import android.content.pm.StagedApexInfo;
 
 /**
  * Parallel implementation of certain {@link PackageManager} APIs that need to
@@ -123,4 +125,24 @@
      * requested version.
      */
     boolean hasSystemFeature(in String featureName, in int version);
+
+    /** Register a observer for change in set of staged APEX ready for installation */
+    void registerStagedApexObserver(in IStagedApexObserver observer);
+
+    /**
+     * Unregister an existing staged apex observer.
+     * This does nothing if this observer was not already registered.
+     */
+    void unregisterStagedApexObserver(in IStagedApexObserver observer);
+
+    /**
+     * Get APEX module names of all APEX that are staged ready for installation
+     */
+    @utf8InCpp String[] getStagedApexModuleNames();
+
+    /**
+     * Get information of APEX which is staged ready for installation.
+     * Returns null if no such APEX is found.
+     */
+    StagedApexInfo getStagedApexInfo(in @utf8InCpp String moduleName);
 }
diff --git a/libs/ui/Size.cpp b/libs/binder/aidl/android/content/pm/IStagedApexObserver.aidl
similarity index 64%
rename from libs/ui/Size.cpp
rename to libs/binder/aidl/android/content/pm/IStagedApexObserver.aidl
index d2996d1..9906436 100644
--- a/libs/ui/Size.cpp
+++ b/libs/binder/aidl/android/content/pm/IStagedApexObserver.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,15 @@
  * limitations under the License.
  */
 
-#include <ui/Size.h>
+package android.content.pm;
 
-namespace android::ui {
+import android.content.pm.ApexStagedEvent;
 
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+/**
+ * This is a non-blocking notification when set of staged apex has changed
+ *
+ * @hide
+ */
+oneway interface IStagedApexObserver {
+  void onApexStaged(in ApexStagedEvent event);
+}
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
new file mode 100644
index 0000000..ece7989
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+/**
+ * This object is designed for returning information regarding
+ * staged APEX that are ready to be installed on next reboot.
+ *
+ * @hide
+ */
+parcelable StagedApexInfo {
+  @utf8InCpp String moduleName;
+  @utf8InCpp String diskImagePath;
+  long versionCode;
+  @utf8InCpp String versionName;
+}
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 2524c5f..3026921 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -14,12 +14,14 @@
         address: true,
     },
     srcs: [
+        "cast_test.cpp",
         "Flags_test.cpp",
         "future_test.cpp",
         "NamedEnum_test.cpp",
         "small_map_test.cpp",
         "small_vector_test.cpp",
         "static_vector_test.cpp",
+        "string_test.cpp",
     ],
     cflags: [
         "-Wall",
diff --git a/libs/ftl/cast_test.cpp b/libs/ftl/cast_test.cpp
new file mode 100644
index 0000000..2abcb8f
--- /dev/null
+++ b/libs/ftl/cast_test.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/cast.h>
+#include <gtest/gtest.h>
+
+#include <cfloat>
+#include <cmath>
+#include <limits>
+
+namespace android::test {
+
+using ftl::cast_safety;
+using ftl::CastSafety;
+
+template <typename T>
+constexpr T min = std::numeric_limits<T>::lowest();
+
+template <typename T>
+constexpr T max = std::numeric_limits<T>::max();
+
+template <typename T>
+constexpr T inf = std::numeric_limits<T>::infinity();
+
+template <typename T>
+constexpr T NaN = std::numeric_limits<T>::quiet_NaN();
+
+// Keep in sync with example usage in header file.
+
+static_assert(cast_safety<uint8_t>(-1) == CastSafety::kUnderflow);
+static_assert(cast_safety<int8_t>(128u) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(static_cast<float>(INT32_MAX)) == CastSafety::kOverflow);
+
+static_assert(cast_safety<float>(-DBL_MAX) == CastSafety::kUnderflow);
+
+// Unsigned to unsigned.
+
+static_assert(cast_safety<uint8_t>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<uint8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint32_t>(max<uint64_t>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>) + 1) ==
+              CastSafety::kOverflow);
+
+// Unsigned to signed.
+
+static_assert(cast_safety<int16_t>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<int16_t>(max<uint8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<int16_t>(max<uint16_t>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) - 1) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) + 1) ==
+              CastSafety::kOverflow);
+
+// Signed to unsigned.
+
+static_assert(cast_safety<uint16_t>(0) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<int8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<int16_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint32_t>(-1) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(max<int64_t>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) - 1) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) + 1) ==
+              CastSafety::kOverflow);
+
+// Signed to signed.
+
+static_assert(cast_safety<int8_t>(-129) == CastSafety::kUnderflow);
+static_assert(cast_safety<int8_t>(-128) == CastSafety::kSafe);
+static_assert(cast_safety<int8_t>(127) == CastSafety::kSafe);
+static_assert(cast_safety<int8_t>(128) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int32_t>(static_cast<int64_t>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<int64_t>(max<int32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<int16_t>(min<int32_t>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(max<int64_t>) == CastSafety::kOverflow);
+
+// Float to float.
+
+static_assert(cast_safety<double>(max<float>) == CastSafety::kSafe);
+static_assert(cast_safety<double>(min<float>) == CastSafety::kSafe);
+
+static_assert(cast_safety<float>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<float>(max<double>) == CastSafety::kOverflow);
+
+TEST(CastSafety, FloatToFloat) {
+  EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(min<float>), min<double>)),
+            CastSafety::kUnderflow);
+  EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(max<float>), max<double>)),
+            CastSafety::kOverflow);
+}
+
+// Unsigned to float.
+
+static_assert(cast_safety<float>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<float>(max<uint64_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<double>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<double>(max<uint64_t>) == CastSafety::kSafe);
+
+// Signed to float.
+
+static_assert(cast_safety<float>(min<int64_t>) == CastSafety::kSafe);
+static_assert(cast_safety<float>(max<int64_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<double>(min<int64_t>) == CastSafety::kSafe);
+static_assert(cast_safety<double>(max<int64_t>) == CastSafety::kSafe);
+
+// Float to unsigned.
+
+static_assert(cast_safety<uint32_t>(0.f) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(min<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(max<float>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow);
+
+static_assert(cast_safety<uint16_t>(-inf<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(inf<float>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(NaN<float>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<float>(max<uint32_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<uint32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<double>(max<uint32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint64_t>(0.0) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint64_t>(max<double>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(-.1) == CastSafety::kUnderflow);
+
+static_assert(cast_safety<uint64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(static_cast<float>(max<uint64_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(static_cast<double>(max<uint64_t>)) == CastSafety::kOverflow);
+
+// Float to signed.
+
+static_assert(cast_safety<int32_t>(0.f) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(min<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(max<float>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int16_t>(-inf<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(inf<double>) == CastSafety::kOverflow);
+static_assert(cast_safety<int64_t>(NaN<double>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int32_t>(static_cast<float>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<int32_t>(static_cast<double>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<int64_t>(0.0) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int64_t>(max<double>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int64_t>(static_cast<float>(min<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<int64_t>(static_cast<double>(min<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kOverflow);
+
+TEST(CastSafety, FloatToSigned) {
+  constexpr int32_t kMax = ftl::details::safe_limits<int32_t, float>::max();
+  static_assert(kMax == 2'147'483'520);
+  EXPECT_EQ(kMax, static_cast<int32_t>(std::nexttowardf(max<int32_t>, 0)));
+
+  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, 0)), CastSafety::kSafe);
+  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, 0)), CastSafety::kSafe);
+  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, 0)), CastSafety::kSafe);
+  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, 0)), CastSafety::kSafe);
+
+  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, min<float>)),
+            CastSafety::kUnderflow);
+  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, max<float>)),
+            CastSafety::kOverflow);
+  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, min<double>)),
+            CastSafety::kUnderflow);
+  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, max<double>)),
+            CastSafety::kOverflow);
+}
+
+}  // namespace android::test
diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp
index 323b9f9..2e81022 100644
--- a/libs/ftl/small_map_test.cpp
+++ b/libs/ftl/small_map_test.cpp
@@ -18,6 +18,9 @@
 #include <gtest/gtest.h>
 
 #include <cctype>
+#include <string>
+
+using namespace std::string_literals;
 
 namespace android::test {
 
@@ -35,16 +38,19 @@
 
   EXPECT_TRUE(map.contains(123));
 
-  EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u);
+  EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u);
 
-  const auto opt = map.find(-1);
+  const auto opt = map.get(-1);
   ASSERT_TRUE(opt);
 
   std::string& ref = *opt;
   EXPECT_TRUE(ref.empty());
   ref = "xyz";
 
-  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+  map.emplace_or_replace(0, "vanilla", 2u, 3u);
+  EXPECT_TRUE(map.dynamic());
+
+  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
 }
 
 TEST(SmallMap, Construct) {
@@ -90,42 +96,253 @@
   }
 }
 
+TEST(SmallMap, UniqueKeys) {
+  {
+    // Duplicate mappings are discarded.
+    const SmallMap map = ftl::init::map<int, float>(1)(2)(3)(2)(3)(1)(3)(2)(1);
+
+    EXPECT_EQ(map.size(), 3u);
+    EXPECT_EQ(map.max_size(), 9u);
+
+    using Map = decltype(map);
+    EXPECT_EQ(map, Map(ftl::init::map(1, 0.f)(2, 0.f)(3, 0.f)));
+  }
+  {
+    // Duplicate mappings may be reordered.
+    const SmallMap map = ftl::init::map('a', 'A')(
+        'b', 'B')('b')('b')('c', 'C')('a')('d')('c')('e', 'E')('d', 'D')('a')('f', 'F');
+
+    EXPECT_EQ(map.size(), 6u);
+    EXPECT_EQ(map.max_size(), 12u);
+
+    using Map = decltype(map);
+    EXPECT_EQ(map, Map(ftl::init::map('a', 'A')('b', 'B')('c', 'C')('d', 'D')('e', 'E')('f', 'F')));
+  }
+}
+
 TEST(SmallMap, Find) {
   {
     // Constant reference.
-    const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+    const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
 
-    const auto opt = map.find('b');
+    const auto opt = map.get('b');
     EXPECT_EQ(opt, 'B');
 
     const char d = 'D';
-    const auto ref = map.find('d').value_or(std::cref(d));
+    const auto ref = map.get('d').value_or(std::cref(d));
     EXPECT_EQ(ref.get(), 'D');
   }
   {
     // Mutable reference.
-    ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+    SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
 
-    const auto opt = map.find('c');
+    const auto opt = map.get('c');
     EXPECT_EQ(opt, 'C');
 
     char d = 'd';
-    const auto ref = map.find('d').value_or(std::ref(d));
+    const auto ref = map.get('d').value_or(std::ref(d));
     ref.get() = 'D';
     EXPECT_EQ(d, 'D');
   }
   {
     // Constant unary operation.
-    const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
-    EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z');
+    const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+    EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z');
   }
   {
     // Mutable unary operation.
-    ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
-    EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); }));
+    SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+    EXPECT_TRUE(map.get('c', [](char& c) { c = std::toupper(c); }));
 
     EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
   }
 }
 
+TEST(SmallMap, TryEmplace) {
+  SmallMap<int, std::string, 3> map;
+  using Pair = decltype(map)::value_type;
+
+  {
+    const auto [it, ok] = map.try_emplace(123, "abc");
+    ASSERT_TRUE(ok);
+    EXPECT_EQ(*it, Pair(123, "abc"s));
+  }
+  {
+    const auto [it, ok] = map.try_emplace(42, 3u, '?');
+    ASSERT_TRUE(ok);
+    EXPECT_EQ(*it, Pair(42, "???"s));
+  }
+  {
+    const auto [it, ok] = map.try_emplace(-1);
+    ASSERT_TRUE(ok);
+    EXPECT_EQ(*it, Pair(-1, std::string()));
+    EXPECT_FALSE(map.dynamic());
+  }
+  {
+    // Insertion fails if mapping exists.
+    const auto [it, ok] = map.try_emplace(42, "!!!");
+    EXPECT_FALSE(ok);
+    EXPECT_EQ(*it, Pair(42, "???"));
+    EXPECT_FALSE(map.dynamic());
+  }
+  {
+    // Insertion at capacity promotes the map.
+    const auto [it, ok] = map.try_emplace(999, "xyz");
+    ASSERT_TRUE(ok);
+    EXPECT_EQ(*it, Pair(999, "xyz"));
+    EXPECT_TRUE(map.dynamic());
+  }
+
+  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s)));
+}
+
+namespace {
+
+// The mapped type does not require a copy/move assignment operator.
+struct String {
+  template <typename... Args>
+  String(Args... args) : str(args...) {}
+  const std::string str;
+
+  bool operator==(const String& other) const { return other.str == str; }
+};
+
+}  // namespace
+
+TEST(SmallMap, TryReplace) {
+  SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
+  using Pair = decltype(map)::value_type;
+
+  {
+    // Replacing fails unless mapping exists.
+    const auto it = map.try_replace(3, "c");
+    EXPECT_EQ(it, map.end());
+  }
+  {
+    // Replacement arguments can refer to the replaced mapping.
+    const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+    ASSERT_TRUE(ref);
+
+    // Construct std::string from one character.
+    const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
+    ASSERT_NE(it, map.end());
+    EXPECT_EQ(*it, Pair(2, "b"));
+  }
+
+  EXPECT_FALSE(map.dynamic());
+  EXPECT_TRUE(map.try_emplace(3, "abc").second);
+  EXPECT_TRUE(map.try_emplace(4, "d").second);
+  EXPECT_TRUE(map.dynamic());
+
+  {
+    // Replacing fails unless mapping exists.
+    const auto it = map.try_replace(5, "e");
+    EXPECT_EQ(it, map.end());
+  }
+  {
+    // Replacement arguments can refer to the replaced mapping.
+    const auto ref = map.get(3);
+    ASSERT_TRUE(ref);
+
+    // Construct std::string from substring.
+    const auto it = map.try_replace(3, ref->get().str, 2u, 1u);
+    ASSERT_NE(it, map.end());
+    EXPECT_EQ(*it, Pair(3, "c"));
+  }
+
+  EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
+}
+
+TEST(SmallMap, EmplaceOrReplace) {
+  SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
+  using Pair = decltype(map)::value_type;
+
+  {
+    // New mapping is emplaced.
+    const auto [it, emplace] = map.emplace_or_replace(3, "c");
+    EXPECT_TRUE(emplace);
+    EXPECT_EQ(*it, Pair(3, "c"));
+  }
+  {
+    // Replacement arguments can refer to the replaced mapping.
+    const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+    ASSERT_TRUE(ref);
+
+    // Construct std::string from one character.
+    const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
+    EXPECT_FALSE(emplace);
+    EXPECT_EQ(*it, Pair(2, "b"));
+  }
+
+  EXPECT_FALSE(map.dynamic());
+  EXPECT_FALSE(map.emplace_or_replace(3, "abc").second);  // Replace.
+  EXPECT_TRUE(map.emplace_or_replace(4, "d").second);     // Emplace.
+  EXPECT_TRUE(map.dynamic());
+
+  {
+    // New mapping is emplaced.
+    const auto [it, emplace] = map.emplace_or_replace(5, "e");
+    EXPECT_TRUE(emplace);
+    EXPECT_EQ(*it, Pair(5, "e"));
+  }
+  {
+    // Replacement arguments can refer to the replaced mapping.
+    const auto ref = map.get(3);
+    ASSERT_TRUE(ref);
+
+    // Construct std::string from substring.
+    const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u);
+    EXPECT_FALSE(emplace);
+    EXPECT_EQ(*it, Pair(3, "c"));
+  }
+
+  EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
+}
+
+TEST(SmallMap, Erase) {
+  {
+    SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4');
+    EXPECT_FALSE(map.dynamic());
+
+    EXPECT_FALSE(map.erase(0));  // Key not found.
+
+    EXPECT_TRUE(map.erase(2));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
+
+    EXPECT_TRUE(map.erase(1));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
+
+    EXPECT_TRUE(map.erase(4));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
+
+    EXPECT_TRUE(map.erase(3));
+    EXPECT_FALSE(map.erase(3));  // Key not found.
+
+    EXPECT_TRUE(map.empty());
+    EXPECT_FALSE(map.dynamic());
+  }
+  {
+    SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
+    map.try_emplace(4, '4');
+    EXPECT_TRUE(map.dynamic());
+
+    EXPECT_FALSE(map.erase(0));  // Key not found.
+
+    EXPECT_TRUE(map.erase(2));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
+
+    EXPECT_TRUE(map.erase(1));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
+
+    EXPECT_TRUE(map.erase(4));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
+
+    EXPECT_TRUE(map.erase(3));
+    EXPECT_FALSE(map.erase(3));  // Key not found.
+
+    EXPECT_TRUE(map.empty());
+    EXPECT_TRUE(map.dynamic());
+  }
+}
+
 }  // namespace android::test
diff --git a/libs/ftl/string_test.cpp b/libs/ftl/string_test.cpp
new file mode 100644
index 0000000..f3d85c8
--- /dev/null
+++ b/libs/ftl/string_test.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/string.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <limits>
+#include <sstream>
+#include <type_traits>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(String, ToChars) {
+  ftl::to_chars_buffer_t<> buffer;
+
+  EXPECT_EQ(ftl::to_chars(buffer, 123u), "123");
+  EXPECT_EQ(ftl::to_chars(buffer, -42, ftl::Radix::kBin), "-0b101010");
+  EXPECT_EQ(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex), "0xcafe");
+  EXPECT_EQ(ftl::to_chars(buffer, '*', ftl::Radix::kHex), "0x2a");
+}
+
+namespace {
+
+template <typename F, typename T>
+void ToCharsTest() {
+  constexpr auto kRadix = F::kRadix;
+
+  using Limits = std::numeric_limits<T>;
+  constexpr auto kMin = Limits::min();
+  constexpr auto kMax = Limits::max();
+  constexpr auto kNeg = static_cast<T>(-42);
+  constexpr auto kPos = static_cast<T>(123);
+
+  ftl::to_chars_buffer_t<T> buffer;
+
+  EXPECT_EQ(ftl::to_chars(buffer, kMin, kRadix), F{}(kMin));
+  EXPECT_EQ(ftl::to_chars(buffer, kMax, kRadix), F{}(kMax));
+  EXPECT_EQ(ftl::to_chars(buffer, kNeg, kRadix), F{}(kNeg));
+  EXPECT_EQ(ftl::to_chars(buffer, kPos, kRadix), F{}(kPos));
+}
+
+template <typename...>
+struct Types {};
+
+template <typename F, typename Types>
+struct ToCharsTests;
+
+template <typename F, typename T, typename... Ts>
+struct ToCharsTests<F, Types<T, Ts...>> {
+  static void test() {
+    ToCharsTest<F, T>();
+    ToCharsTests<F, Types<Ts...>>::test();
+  }
+};
+
+template <typename F>
+struct ToCharsTests<F, Types<>> {
+  static void test() {}
+};
+
+template <typename T, typename U = std::make_unsigned_t<T>>
+U to_unsigned(std::ostream& stream, T v) {
+  if (std::is_same_v<T, U>) return v;
+
+  if (v < 0) {
+    stream << '-';
+    return std::numeric_limits<U>::max() - static_cast<U>(v) + 1;
+  } else {
+    return static_cast<U>(v);
+  }
+}
+
+struct Bin {
+  static constexpr auto kRadix = ftl::Radix::kBin;
+
+  template <typename T>
+  std::string operator()(T v) const {
+    std::ostringstream stream;
+    auto u = to_unsigned(stream, v);
+    stream << "0b";
+
+    if (u == 0) {
+      stream << 0;
+    } else {
+      std::ostringstream digits;
+      do {
+        digits << (u & 1);
+      } while (u >>= 1);
+
+      const auto str = digits.str();
+      std::copy(str.rbegin(), str.rend(), std::ostream_iterator<char>(stream));
+    }
+
+    return stream.str();
+  }
+};
+
+struct Dec {
+  static constexpr auto kRadix = ftl::Radix::kDec;
+
+  template <typename T>
+  std::string operator()(T v) const {
+    return std::to_string(v);
+  }
+};
+
+struct Hex {
+  static constexpr auto kRadix = ftl::Radix::kHex;
+
+  template <typename T>
+  std::string operator()(T v) const {
+    std::ostringstream stream;
+    const auto u = to_unsigned(stream, v);
+    stream << "0x" << std::hex << std::nouppercase;
+    stream << (sizeof(T) == 1 ? static_cast<unsigned>(u) : u);
+    return stream.str();
+  }
+};
+
+using IntegerTypes =
+    Types<char, unsigned char, signed char, std::uint8_t, std::uint16_t, std::uint32_t,
+          std::uint64_t, std::int8_t, std::int16_t, std::int32_t, std::int64_t>;
+
+}  // namespace
+
+TEST(String, ToCharsBin) {
+  ToCharsTests<Bin, IntegerTypes>::test();
+
+  {
+    const std::uint8_t x = 0b1111'1111;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "0b11111111");
+  }
+  {
+    const std::int16_t x = -0b1000'0000'0000'0000;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "-0b1000000000000000");
+  }
+}
+
+TEST(String, ToCharsDec) {
+  ToCharsTests<Dec, IntegerTypes>::test();
+
+  {
+    const std::uint32_t x = UINT32_MAX;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x), "4294967295");
+  }
+  {
+    const std::int32_t x = INT32_MIN;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x), "-2147483648");
+  }
+}
+
+TEST(String, ToCharsHex) {
+  ToCharsTests<Hex, IntegerTypes>::test();
+
+  {
+    const std::uint16_t x = 0xfade;
+    ftl::to_chars_buffer_t<decltype(x)> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kHex), "0xfade");
+  }
+  {
+    ftl::to_chars_buffer_t<> buffer;
+    EXPECT_EQ(ftl::to_chars(buffer, INT64_MIN, ftl::Radix::kHex), "-0x8000000000000000");
+  }
+}
+
+}  // namespace android::test
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index d54de49..7f0cac5 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -343,80 +343,6 @@
     return nullptr;
 }
 
-bool GraphicsEnv::checkAngleRules(void* so) {
-    auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
-    auto model = base::GetProperty("ro.product.model", "UNSET");
-
-    auto ANGLEGetFeatureSupportUtilAPIVersion =
-            (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
-                                                          "ANGLEGetFeatureSupportUtilAPIVersion");
-
-    if (!ANGLEGetFeatureSupportUtilAPIVersion) {
-        ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function");
-        return false;
-    }
-
-    // Negotiate the interface version by requesting most recent known to the platform
-    unsigned int versionToUse = CURRENT_ANGLE_API_VERSION;
-    if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) {
-        ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, "
-              "requested version %u",
-              versionToUse);
-        return false;
-    }
-
-    // Add and remove versions below as needed
-    bool useAngle = false;
-    switch (versionToUse) {
-        case 2: {
-            ALOGV("Using version %d of ANGLE feature-support library", versionToUse);
-            void* rulesHandle = nullptr;
-            int rulesVersion = 0;
-            void* systemInfoHandle = nullptr;
-
-            // Get the symbols for the feature-support-utility library:
-#define GET_SYMBOL(symbol)                                                 \
-    fp##symbol symbol = (fp##symbol)dlsym(so, #symbol);                    \
-    if (!symbol) {                                                         \
-        ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \
-        break;                                                             \
-    }
-            GET_SYMBOL(ANGLEAndroidParseRulesString);
-            GET_SYMBOL(ANGLEGetSystemInfo);
-            GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo);
-            GET_SYMBOL(ANGLEShouldBeUsedForApplication);
-            GET_SYMBOL(ANGLEFreeRulesHandle);
-            GET_SYMBOL(ANGLEFreeSystemInfoHandle);
-
-            // Parse the rules, obtain the SystemInfo, and evaluate the
-            // application against the rules:
-            if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) {
-                ALOGW("ANGLE feature-support library cannot parse rules file");
-                break;
-            }
-            if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) {
-                ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
-                break;
-            }
-            if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
-                                                  systemInfoHandle)) {
-                ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
-                break;
-            }
-            useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion,
-                                                         systemInfoHandle, mAngleAppName.c_str());
-            (ANGLEFreeRulesHandle)(rulesHandle);
-            (ANGLEFreeSystemInfoHandle)(systemInfoHandle);
-        } break;
-
-        default:
-            ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse);
-    }
-
-    ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
-    return useAngle;
-}
-
 bool GraphicsEnv::shouldUseAngle(std::string appName) {
     if (appName != mAngleAppName) {
         // Make sure we are checking the app we were init'ed for
@@ -444,31 +370,20 @@
     const char* ANGLE_PREFER_ANGLE = "angle";
     const char* ANGLE_PREFER_NATIVE = "native";
 
+    mUseAngle = NO;
     if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
         ALOGV("User set \"Developer Options\" to force the use of ANGLE");
         mUseAngle = YES;
     } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
         ALOGV("User set \"Developer Options\" to force the use of Native");
-        mUseAngle = NO;
     } else {
-        // The "Developer Options" value wasn't set to force the use of ANGLE.  Need to temporarily
-        // load ANGLE and call the updatable opt-in/out logic:
-        void* featureSo = loadLibrary("feature_support");
-        if (featureSo) {
-            ALOGV("loaded ANGLE's opt-in/out logic from namespace");
-            mUseAngle = checkAngleRules(featureSo) ? YES : NO;
-            dlclose(featureSo);
-            featureSo = nullptr;
-        } else {
-            ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE.");
-        }
+        ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str());
     }
 }
 
 void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
                                const std::string developerOptIn,
-                               const std::vector<std::string> eglFeatures, const int rulesFd,
-                               const long rulesOffset, const long rulesLength) {
+                               const std::vector<std::string> eglFeatures) {
     if (mUseAngle != UNKNOWN) {
         // We've already figured out an answer for this app, so just return.
         ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
@@ -485,22 +400,6 @@
     ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());
     mAngleDeveloperOptIn = developerOptIn;
 
-    lseek(rulesFd, rulesOffset, SEEK_SET);
-    mRulesBuffer = std::vector<char>(rulesLength + 1);
-    ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength);
-    if (numBytesRead < 0) {
-        ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead);
-        numBytesRead = 0;
-    } else if (numBytesRead == 0) {
-        ALOGW("Empty rules file");
-    }
-    if (numBytesRead != rulesLength) {
-        ALOGW("Did not read all of the necessary bytes from the rules file."
-              "expected: %ld, got: %zd",
-              rulesLength, numBytesRead);
-    }
-    mRulesBuffer[numBytesRead] = '\0';
-
     // Update the current status of whether we should use ANGLE or not
     updateUseAngle();
 }
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 900fc49..56d1139 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -97,8 +97,7 @@
     // in the search path must have a '!' after the zip filename, e.g.
     //     /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
     void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
-                      const std::vector<std::string> eglFeatures, const int rulesFd,
-                      const long rulesOffset, const long rulesLength);
+                      const std::vector<std::string> eglFeatures);
     // Get the ANGLE driver namespace.
     android_namespace_t* getAngleNamespace();
     // Get the app name for ANGLE debug message.
@@ -129,8 +128,6 @@
 
     // Load requested ANGLE library.
     void* loadLibrary(std::string name);
-    // Check ANGLE support with the rules.
-    bool checkAngleRules(void* so);
     // Update whether ANGLE should be used.
     void updateUseAngle();
     // Link updatable driver namespace with llndk and vndk-sp libs.
@@ -159,8 +156,6 @@
     std::string mAngleDeveloperOptIn;
     // ANGLE EGL features;
     std::vector<std::string> mAngleEglFeatures;
-    // ANGLE rules.
-    std::vector<char> mRulesBuffer;
     // Use ANGLE flag.
     UseAngle mUseAngle = UNKNOWN;
     // Vulkan debug layers libs.
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2a980bd..3bf6306 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -124,11 +124,11 @@
         return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
     }
 
-    status_t captureDisplay(uint64_t displayOrLayerStack,
+    status_t captureDisplay(DisplayId displayId,
                             const sp<IScreenCaptureListener>& captureListener) override {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        SAFE_PARCEL(data.writeUint64, displayOrLayerStack);
+        SAFE_PARCEL(data.writeUint64, displayId.value);
         SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
 
         return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
@@ -282,9 +282,14 @@
             NO_ERROR) {
             std::vector<uint64_t> rawIds;
             if (reply.readUint64Vector(&rawIds) == NO_ERROR) {
-                std::vector<PhysicalDisplayId> displayIds(rawIds.size());
-                std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(),
-                               [](uint64_t rawId) { return PhysicalDisplayId(rawId); });
+                std::vector<PhysicalDisplayId> displayIds;
+                displayIds.reserve(rawIds.size());
+
+                for (const uint64_t rawId : rawIds) {
+                    if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) {
+                        displayIds.push_back(*id);
+                    }
+                }
                 return displayIds;
             }
         }
@@ -1344,12 +1349,15 @@
         }
         case CAPTURE_DISPLAY_BY_ID: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            uint64_t displayOrLayerStack = 0;
+            uint64_t value;
+            SAFE_PARCEL(data.readUint64, &value);
+            const auto id = DisplayId::fromValue(value);
+            if (!id) return BAD_VALUE;
+
             sp<IScreenCaptureListener> captureListener;
-            SAFE_PARCEL(data.readUint64, &displayOrLayerStack);
             SAFE_PARCEL(data.readStrongBinder, &captureListener);
 
-            return captureDisplay(displayOrLayerStack, captureListener);
+            return captureDisplay(*id, captureListener);
         }
         case CAPTURE_LAYERS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1416,9 +1424,9 @@
         }
         case GET_PHYSICAL_DISPLAY_TOKEN: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            PhysicalDisplayId displayId(data.readUint64());
-            sp<IBinder> display = getPhysicalDisplayToken(displayId);
-            reply->writeStrongBinder(display);
+            const auto id = DisplayId::fromValue<PhysicalDisplayId>(data.readUint64());
+            if (!id) return BAD_VALUE;
+            reply->writeStrongBinder(getPhysicalDisplayToken(*id));
             return NO_ERROR;
         }
         case GET_DISPLAY_STATE: {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 8a7a871..1fd9d13 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -41,7 +41,6 @@
         z(0),
         w(0),
         h(0),
-        layerStack(0),
         alpha(0),
         flags(0),
         mask(0),
@@ -86,7 +85,7 @@
     SAFE_PARCEL(output.writeInt32, z);
     SAFE_PARCEL(output.writeUint32, w);
     SAFE_PARCEL(output.writeUint32, h);
-    SAFE_PARCEL(output.writeUint32, layerStack);
+    SAFE_PARCEL(output.writeUint32, layerStack.id);
     SAFE_PARCEL(output.writeFloat, alpha);
     SAFE_PARCEL(output.writeUint32, flags);
     SAFE_PARCEL(output.writeUint32, mask);
@@ -188,7 +187,7 @@
     SAFE_PARCEL(input.readInt32, &z);
     SAFE_PARCEL(input.readUint32, &w);
     SAFE_PARCEL(input.readUint32, &h);
-    SAFE_PARCEL(input.readUint32, &layerStack);
+    SAFE_PARCEL(input.readUint32, &layerStack.id);
     SAFE_PARCEL(input.readFloat, &alpha);
 
     SAFE_PARCEL(input.readUint32, &flags);
@@ -316,21 +315,14 @@
     return state.read(input);
 }
 
-DisplayState::DisplayState()
-      : what(0),
-        layerStack(0),
-        flags(0),
-        layerStackSpaceRect(Rect::EMPTY_RECT),
-        orientedDisplaySpaceRect(Rect::EMPTY_RECT),
-        width(0),
-        height(0) {}
+DisplayState::DisplayState() = default;
 
 status_t DisplayState::write(Parcel& output) const {
     SAFE_PARCEL(output.writeStrongBinder, token);
     SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface));
     SAFE_PARCEL(output.writeUint32, what);
-    SAFE_PARCEL(output.writeUint32, layerStack);
     SAFE_PARCEL(output.writeUint32, flags);
+    SAFE_PARCEL(output.writeUint32, layerStack.id);
     SAFE_PARCEL(output.writeUint32, toRotationInt(orientation));
     SAFE_PARCEL(output.write, layerStackSpaceRect);
     SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
@@ -346,8 +338,8 @@
     surface = interface_cast<IGraphicBufferProducer>(tmpBinder);
 
     SAFE_PARCEL(input.readUint32, &what);
-    SAFE_PARCEL(input.readUint32, &layerStack);
     SAFE_PARCEL(input.readUint32, &flags);
+    SAFE_PARCEL(input.readUint32, &layerStack.id);
     uint32_t tmpUint = 0;
     SAFE_PARCEL(input.readUint32, &tmpUint);
     orientation = ui::toRotation(tmpUint);
@@ -563,10 +555,13 @@
         what |= eDestinationFrameChanged;
         destinationFrame = other.destinationFrame;
     }
+    if (other.what & eProducerDisconnect) {
+        what |= eProducerDisconnect;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
-              "other.what=0x%" PRIu64 " what=0x%" PRIu64,
-              other.what, what);
+              "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
+              other.what, what, (other.what & what) ^ other.what);
     }
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index dc71b6a..05554ca 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1095,7 +1095,7 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayerStack(
-        const sp<SurfaceControl>& sc, uint32_t layerStack) {
+        const sp<SurfaceControl>& sc, ui::LayerStack layerStack) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
@@ -1760,7 +1760,7 @@
 }
 
 void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp<IBinder>& token,
-        uint32_t layerStack) {
+                                                              ui::LayerStack layerStack) {
     DisplayState& s(getDisplayState(token));
     s.layerStack = layerStack;
     s.what |= DisplayState::eLayerStackChanged;
@@ -2184,12 +2184,12 @@
     return s->captureDisplay(captureArgs, captureListener);
 }
 
-status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack,
+status_t ScreenshotClient::captureDisplay(DisplayId displayId,
                                           const sp<IScreenCaptureListener>& captureListener) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
 
-    return s->captureDisplay(displayOrLayerStack, captureListener);
+    return s->captureDisplay(displayId, captureListener);
 }
 
 status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index ad7bcb7..b7cd082 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -116,6 +116,11 @@
 
     using EventRegistrationFlags = Flags<EventRegistration>;
 
+    template <typename T>
+    struct SpHash {
+        size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); }
+    };
+
     /*
      * Create a connection with SurfaceFlinger.
      */
@@ -239,24 +244,17 @@
      * The subregion can be optionally rotated.  It will also be scaled to
      * match the size of the output buffer.
      */
-    virtual status_t captureDisplay(const DisplayCaptureArgs& args,
-                                    const sp<IScreenCaptureListener>& captureListener) = 0;
+    virtual status_t captureDisplay(const DisplayCaptureArgs&,
+                                    const sp<IScreenCaptureListener>&) = 0;
 
-    virtual status_t captureDisplay(uint64_t displayOrLayerStack,
-                                    const sp<IScreenCaptureListener>& captureListener) = 0;
-
-    template <class AA>
-    struct SpHash {
-        size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); }
-    };
+    virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0;
 
     /**
      * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
      * This requires READ_FRAME_BUFFER permission. This function will fail if there
      * is a secure window on screen
      */
-    virtual status_t captureLayers(const LayerCaptureArgs& args,
-                                   const sp<IScreenCaptureListener>& captureListener) = 0;
+    virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0;
 
     /* Clears the frame statistics for animations.
      *
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 8dc8b9a..f14127c 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -35,6 +35,7 @@
 #include <math/vec3.h>
 #include <ui/BlurRegion.h>
 #include <ui/GraphicTypes.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Rotation.h>
@@ -143,7 +144,7 @@
     int32_t z;
     uint32_t w;
     uint32_t h;
-    uint32_t layerStack;
+    ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
     float alpha;
     uint32_t flags;
     uint32_t mask;
@@ -267,11 +268,12 @@
     DisplayState();
     void merge(const DisplayState& other);
 
-    uint32_t what;
+    uint32_t what = 0;
+    uint32_t flags = 0;
     sp<IBinder> token;
     sp<IGraphicBufferProducer> surface;
-    uint32_t layerStack;
-    uint32_t flags;
+
+    ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
 
     // These states define how layers are projected onto the physical display.
     //
@@ -285,10 +287,11 @@
     // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,
     // 0).
     ui::Rotation orientation = ui::ROTATION_0;
-    Rect layerStackSpaceRect;
-    Rect orientedDisplaySpaceRect;
+    Rect layerStackSpaceRect = Rect::EMPTY_RECT;
+    Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;
 
-    uint32_t width, height;
+    uint32_t width = 0;
+    uint32_t height = 0;
 
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 1d758e0..a980ce2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -456,7 +456,7 @@
                                              int backgroundBlurRadius);
         Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
                                     const std::vector<BlurRegion>& regions);
-        Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
+        Transaction& setLayerStack(const sp<SurfaceControl>&, ui::LayerStack);
         Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
 
         /// Reparents the current layer to the new parent handle. The new parent must not be null.
@@ -566,7 +566,7 @@
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
-        void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack);
+        void setDisplayLayerStack(const sp<IBinder>& token, ui::LayerStack);
 
         void setDisplayFlags(const sp<IBinder>& token, uint32_t flags);
 
@@ -638,12 +638,9 @@
 
 class ScreenshotClient {
 public:
-    static status_t captureDisplay(const DisplayCaptureArgs& captureArgs,
-                                   const sp<IScreenCaptureListener>& captureListener);
-    static status_t captureDisplay(uint64_t displayOrLayerStack,
-                                   const sp<IScreenCaptureListener>& captureListener);
-    static status_t captureLayers(const LayerCaptureArgs& captureArgs,
-                                  const sp<IScreenCaptureListener>& captureListener);
+    static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
+    static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+    static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 9082d27..26d902d 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -128,7 +128,7 @@
         mDisplayToken = mClient->getInternalDisplayToken();
         ASSERT_NE(nullptr, mDisplayToken.get());
         Transaction t;
-        t.setDisplayLayerStack(mDisplayToken, 0);
+        t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
         t.apply();
         t.clear();
 
@@ -142,7 +142,7 @@
                                                  mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
                                                  ISurfaceComposerClient::eFXSurfaceBufferState,
                                                  /*parent*/ nullptr);
-        t.setLayerStack(mSurfaceControl, 0)
+        t.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
                 .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
                 .show(mSurfaceControl)
                 .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
@@ -490,7 +490,7 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect);
     ASSERT_NE(nullptr, bg.get());
     Transaction t;
-    t.setLayerStack(bg, 0)
+    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
             .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
             .setColor(bg, half3{0, 0, 0})
             .setLayer(bg, 0)
@@ -545,7 +545,7 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect);
     ASSERT_NE(nullptr, bg.get());
     Transaction t;
-    t.setLayerStack(bg, 0)
+    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
             .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
             .setColor(bg, half3{0, 0, 0})
             .setLayer(bg, 0)
@@ -612,7 +612,7 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect);
     ASSERT_NE(nullptr, bg.get());
     Transaction t;
-    t.setLayerStack(bg, 0)
+    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
             .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
             .setColor(bg, half3{0, 0, 0})
             .setLayer(bg, 0)
@@ -733,7 +733,7 @@
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
     ASSERT_NE(nullptr, bgSurface.get());
     Transaction t;
-    t.setLayerStack(bgSurface, 0)
+    t.setLayerStack(bgSurface, ui::DEFAULT_LAYER_STACK)
             .show(bgSurface)
             .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
             .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index a02970c..d1ad478 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -752,21 +752,19 @@
     }
     status_t setActiveColorMode(const sp<IBinder>& /*display*/,
         ColorMode /*colorMode*/) override { return NO_ERROR; }
-    status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */,
-                            const sp<IScreenCaptureListener>& /* captureListener */) override {
-        return NO_ERROR;
-    }
     void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
     void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
-    status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
-                            const sp<IScreenCaptureListener>& /* captureListener */) override {
+
+    status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override {
         return NO_ERROR;
     }
-    virtual status_t captureLayers(
-            const LayerCaptureArgs& /* captureArgs */,
-            const sp<IScreenCaptureListener>& /* captureListener */) override {
+    status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override {
         return NO_ERROR;
     }
+    status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override {
+        return NO_ERROR;
+    }
+
     status_t clearAnimationFrameStats() override { return NO_ERROR; }
     status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
         return NO_ERROR;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 35209f7..1e8ff94 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -170,6 +170,9 @@
         case AINPUT_EVENT_TYPE_DRAG: {
             return "DRAG";
         }
+        case AINPUT_EVENT_TYPE_TOUCH_MODE: {
+            return "TOUCH_MODE";
+        }
     }
     return "UNKNOWN";
 }
@@ -330,10 +333,6 @@
     scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale);
 }
 
-void PointerCoords::scale(float globalScaleFactor) {
-    scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
-}
-
 void PointerCoords::applyOffset(float xOffset, float yOffset) {
     setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset);
     setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
@@ -899,6 +898,19 @@
     mY = from.mY;
 }
 
+// --- TouchModeEvent ---
+
+void TouchModeEvent::initialize(int32_t id, bool isInTouchMode) {
+    InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+                           ADISPLAY_ID_NONE, INVALID_HMAC);
+    mIsInTouchMode = isInTouchMode;
+}
+
+void TouchModeEvent::initialize(const TouchModeEvent& from) {
+    InputEvent::initialize(from);
+    mIsInTouchMode = from.mIsInTouchMode;
+}
+
 // --- PooledInputEventFactory ---
 
 PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
@@ -953,6 +965,15 @@
     return event;
 }
 
+TouchModeEvent* PooledInputEventFactory::createTouchModeEvent() {
+    if (mTouchModeEventPool.empty()) {
+        return new TouchModeEvent();
+    }
+    TouchModeEvent* event = mTouchModeEventPool.front().release();
+    mTouchModeEventPool.pop();
+    return event;
+}
+
 void PooledInputEventFactory::recycle(InputEvent* event) {
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY:
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index ea8b9a7..1e93dfb 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -116,6 +116,7 @@
         case Type::FOCUS:
         case Type::CAPTURE:
         case Type::DRAG:
+        case Type::TOUCH_MODE:
             return true;
         case Type::TIMELINE: {
             const nsecs_t gpuCompletedTime =
@@ -151,6 +152,8 @@
             return sizeof(Header) + body.drag.size();
         case Type::TIMELINE:
             return sizeof(Header) + body.timeline.size();
+        case Type::TOUCH_MODE:
+            return sizeof(Header) + body.touchMode.size();
     }
     return sizeof(Header);
 }
@@ -293,6 +296,10 @@
             msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
             break;
         }
+        case InputMessage::Type::TOUCH_MODE: {
+            msg->body.touchMode.eventId = body.touchMode.eventId;
+            msg->body.touchMode.isInTouchMode = body.touchMode.isInTouchMode;
+        }
     }
 }
 
@@ -665,6 +672,22 @@
     return mChannel->sendMessage(&msg);
 }
 
+status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode) {
+    if (ATRACE_ENABLED()) {
+        std::string message =
+                StringPrintf("publishTouchModeEvent(inputChannel=%s, isInTouchMode=%s)",
+                             mChannel->getName().c_str(), toString(isInTouchMode));
+        ATRACE_NAME(message.c_str());
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::Type::TOUCH_MODE;
+    msg.header.seq = seq;
+    msg.body.touchMode.eventId = eventId;
+    msg.body.touchMode.isInTouchMode = isInTouchMode;
+    return mChannel->sendMessage(&msg);
+}
+
 android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
     if (DEBUG_TRANSPORT_ACTIONS) {
         ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
@@ -866,6 +889,16 @@
                 *outEvent = dragEvent;
                 break;
             }
+
+            case InputMessage::Type::TOUCH_MODE: {
+                TouchModeEvent* touchModeEvent = factory->createTouchModeEvent();
+                if (!touchModeEvent) return NO_MEMORY;
+
+                initializeTouchModeEvent(touchModeEvent, &mMsg);
+                *outSeq = mMsg.header.seq;
+                *outEvent = touchModeEvent;
+                break;
+            }
         }
     }
     return OK;
@@ -1370,6 +1403,10 @@
                       msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
 }
 
+void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) {
+    event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode);
+}
+
 void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
     uint32_t pointerCount = msg->body.motion.pointerCount;
     PointerCoords pointerCoords[pointerCount];
@@ -1476,6 +1513,11 @@
                                                        presentTime);
                     break;
                 }
+                case InputMessage::Type::TOUCH_MODE: {
+                    out += android::base::StringPrintf("isInTouchMode=%s",
+                                                       toString(msg.body.touchMode.isInTouchMode));
+                    break;
+                }
             }
             out += "\n";
         }
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 5d1f2c3..8db5bf1 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -56,6 +56,7 @@
     void PublishAndConsumeFocusEvent();
     void PublishAndConsumeCaptureEvent();
     void PublishAndConsumeDragEvent();
+    void PublishAndConsumeTouchModeEvent();
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -413,6 +414,46 @@
             << "finished signal's consume time should be greater than publish time";
 }
 
+void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() {
+    status_t status;
+
+    constexpr uint32_t seq = 15;
+    int32_t eventId = InputEvent::nextId();
+    constexpr bool touchModeEnabled = true;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled);
+    ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK";
+
+    uint32_t consumeSeq;
+    InputEvent* event;
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+    ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType())
+            << "consumer should have returned a touch mode event";
+
+    const TouchModeEvent& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+    EXPECT_EQ(seq, consumeSeq);
+    EXPECT_EQ(eventId, touchModeEvent.getId());
+    EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode());
+
+    status = mConsumer->sendFinishedSignal(seq, true);
+    ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
+            << "finished signal's consume time should be greater than publish time";
+}
+
 TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
     const int32_t inputEventId = 20;
     std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
@@ -449,6 +490,10 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
 }
 
+TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
     status_t status;
     const size_t pointerCount = 1;
@@ -520,6 +565,7 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
 }
 
 } // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 59fed1f..18289a5 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -102,6 +102,10 @@
   CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
   CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
+
+  CHECK_OFFSET(InputMessage::Body::TouchMode, eventId, 0);
+  CHECK_OFFSET(InputMessage::Body::TouchMode, isInTouchMode, 4);
+  CHECK_OFFSET(InputMessage::Body::TouchMode, empty, 5);
 }
 
 void TestHeaderSize() {
@@ -123,6 +127,7 @@
     static_assert(sizeof(InputMessage::Body::Focus) == 8);
     static_assert(sizeof(InputMessage::Body::Capture) == 8);
     static_assert(sizeof(InputMessage::Body::Drag) == 16);
+    static_assert(sizeof(InputMessage::Body::TouchMode) == 8);
     // Timeline
     static_assert(GraphicsTimeline::SIZE == 2);
     static_assert(sizeof(InputMessage::Body::Timeline) == 24);
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 2dd6c4f..6a14d4f 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -305,8 +305,9 @@
         // Fortunately, these events are small so sending packets across the
         // socket should be atomic across processes.
         DisplayEventReceiver::Event event;
-        event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
-                                                           PhysicalDisplayId(0), systemTime()};
+        event.header =
+                DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+                                                    PhysicalDisplayId::fromPort(0), systemTime()};
         injectEvent(event);
     }
 }
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 6eaa84e..bac44c9 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -272,10 +272,11 @@
     status_t attachToContext(uint32_t tex);
 
     sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
-                                    float* outTransformMatrix, bool* outQueueEmpty,
+                                    float* outTransformMatrix, uint32_t* outTransform,
+                                    bool* outQueueEmpty,
                                     SurfaceTexture_createReleaseFence createFence,
                                     SurfaceTexture_fenceWait fenceWait,
-                                    void* fencePassThroughHandle);
+                                    void* fencePassThroughHandle, ARect* currentCrop);
 
     /**
      * takeConsumerOwnership attaches a SurfaceTexture that is currently in the
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index 85fe42f..e85009c 100644
--- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -84,10 +84,11 @@
  */
 AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
                                                android_dataspace* outDataspace,
-                                               float* outTransformMatrix, bool* outNewContent,
+                                               float* outTransformMatrix, uint32_t* outTransform,
+                                               bool* outNewContent,
                                                ASurfaceTexture_createReleaseFence createFence,
                                                ASurfaceTexture_fenceWait fenceWait,
-                                               void* fencePassThroughHandle);
+                                               void* fencePassThroughHandle, ARect* currentCrop);
 
 } // namespace android
 
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 62db6d0..3535e67 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -464,10 +464,11 @@
 }
 
 sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
-                                                float* outTransformMatrix, bool* outQueueEmpty,
+                                                float* outTransformMatrix, uint32_t* outTransform,
+                                                bool* outQueueEmpty,
                                                 SurfaceTexture_createReleaseFence createFence,
                                                 SurfaceTexture_fenceWait fenceWait,
-                                                void* fencePassThroughHandle) {
+                                                void* fencePassThroughHandle, ARect* currentCrop) {
     Mutex::Autolock _l(mMutex);
     sp<GraphicBuffer> buffer;
 
@@ -484,6 +485,8 @@
     buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,
                                           createFence, fenceWait, fencePassThroughHandle);
     memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+    *outTransform = mCurrentTransform;
+    *currentCrop = mCurrentCrop;
     return buffer;
 }
 
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
index c214ab7..cc0a12d 100644
--- a/libs/nativedisplay/surfacetexture/surface_texture.cpp
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -194,15 +194,18 @@
 
 AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
                                                android_dataspace* outDataspace,
-                                               float* outTransformMatrix, bool* outNewContent,
+                                               float* outTransformMatrix, uint32_t* outTransform,
+                                               bool* outNewContent,
                                                ASurfaceTexture_createReleaseFence createFence,
-                                               ASurfaceTexture_fenceWait fenceWait, void* handle) {
+                                               ASurfaceTexture_fenceWait fenceWait, void* handle,
+                                               ARect* currentCrop) {
     sp<GraphicBuffer> buffer;
     *outNewContent = false;
     bool queueEmpty;
     do {
         buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
-                                             &queueEmpty, createFence, fenceWait, handle);
+                                             outTransform, &queueEmpty, createFence, fenceWait,
+                                             handle, currentCrop);
         if (!queueEmpty) {
             *outNewContent = true;
         }
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index d93a84c..d5e7cb2 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -556,6 +556,7 @@
                                    int32_t* _Nonnull outBytesPerPixel,
                                    int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29);
 
+
 /**
  * Get the system wide unique id for an AHardwareBuffer.
  *
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 467f848..b1e1014 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1156,10 +1156,6 @@
 
     const mat4 projectionMatrix =
             ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
-    if (!display.clearRegion.isEmpty()) {
-        glDisable(GL_BLEND);
-        fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
-    }
 
     Mesh mesh = Mesh::Builder()
                         .setPrimitive(Mesh::TRIANGLE_FAN)
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 53fa622..d395d06 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -51,10 +51,6 @@
     // dataspace, in non-linear space.
     mat4 colorTransform = mat4();
 
-    // Region that will be cleared to (0, 0, 0, 1) prior to rendering.
-    // This is specified in layer-stack space.
-    Region clearRegion = Region::INVALID_REGION;
-
     // An additional orientation flag to be applied after clipping the output.
     // By way of example, this may be used for supporting fullscreen screenshot
     // capture of a device in landscape while the buffer is in portrait
@@ -68,8 +64,7 @@
 static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
     return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
             lhs.maxLuminance == rhs.maxLuminance && lhs.outputDataspace == rhs.outputDataspace &&
-            lhs.colorTransform == rhs.colorTransform &&
-            lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+            lhs.colorTransform == rhs.colorTransform && lhs.orientation == rhs.orientation;
 }
 
 // Defining PrintTo helps with Google Tests.
@@ -84,9 +79,6 @@
     PrintTo(settings.outputDataspace, os);
     *os << "\n    .colorTransform = " << settings.colorTransform;
     *os << "\n    .clearRegion = ";
-    PrintTo(settings.clearRegion, os);
-    *os << "\n    .orientation = " << settings.orientation;
-    *os << "\n}";
 }
 
 } // namespace renderengine
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index ae8f238..01df6a6 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -95,25 +95,28 @@
             .alpha = 1,
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer, &caster};
-    // When sourceDataspace matches dest, the general shadow fragment shader doesn't
-    // have color correction added.
-    // independently, when it is not srgb, the *vertex* shader has color correction added.
-    // This may be a bug, but the shader still needs to be cached as it is triggered
-    // during youtube pip.
-    for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
-        layer.sourceDataspace = dataspace;
-        // The 2nd matrix, which has different scales for x and y, will
-        // generate the slower (more general case) shadow shader
-        for (auto transform : {mat4(), kScaleAndTranslate, kFlip}) {
-            layer.geometry.positionTransform = transform;
-            caster.geometry.positionTransform = transform;
-            for (bool translucent : {false, true}){
-                layer.shadow.casterIsTranslucent = translucent;
-                renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                        base::unique_fd(), nullptr);
-            }
-        }
+    // Four combinations of settings are used (two transforms here, and drawShadowLayers is
+    // called with two different destination data spaces) They're all rounded rect.
+    // Three of these are cache misses that generate new shaders.
+    // The first combination generates a short and simple shadow shader.
+    // The second combination, flip transform, generates two shaders. The first appears to involve
+    //   gaussian_fp. The second is a long and general purpose shadow shader with a device space
+    //   transformation stage.
+    // The third combination is a cache hit, nothing new.
+    // The fourth combination, flip transform with a non-SRGB destination dataspace, is new.
+    //   It is unique in that nearly everything is done in the vertex shader, and that vertex shader
+    //   requires color correction. This is triggered differently from every other instance of color
+    //   correction. All other instances are triggered when src and dst dataspaces differ, while
+    //   this one is triggered by the destination being non-srgb. Apparently since the third
+    //   combination is a cache hit, this color correction is only added when the vertex shader is
+    //   doing something non-trivial.
+    for (auto transform : {mat4(), kFlip}) {
+        layer.geometry.positionTransform = transform;
+        caster.geometry.positionTransform = transform;
+        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                base::unique_fd(), &drawFence);
     }
 }
 
@@ -138,6 +141,7 @@
                                           }},
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
         layer.sourceDataspace = dataspace;
@@ -151,7 +155,7 @@
                 for (auto alpha : {half(.2f), half(1.0f)}) {
                     layer.alpha = alpha;
                     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                             base::unique_fd(), nullptr);
+                                             base::unique_fd(), &drawFence);
                 }
             }
         }
@@ -174,13 +178,14 @@
             .alpha = 0.5,
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto transform : {mat4(), kScaleAndTranslate}) {
         layer.geometry.positionTransform = transform;
         for (float roundedCornersRadius : {0.0f, 50.f}) {
             layer.geometry.roundedCornersRadius = roundedCornersRadius;
             renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                     base::unique_fd(), nullptr);
+                                     base::unique_fd(), &drawFence);
         }
     }
 }
@@ -199,12 +204,13 @@
             .skipContentDraw = true,
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     // Different blur code is invoked for radii less and greater than 30 pixels
     for (int radius : {9, 60}) {
         layer.backgroundBlurRadius = radius;
         renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                 base::unique_fd(), nullptr);
+                                 base::unique_fd(), &drawFence);
     }
 }
 
@@ -240,6 +246,7 @@
                     },
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) {
         layer.source = pixelSource;
@@ -251,7 +258,7 @@
                 for (float alpha : {0.5f, 1.f}) {
                     layer.alpha = alpha,
                     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                             base::unique_fd(), nullptr);
+                                             base::unique_fd(), &drawFence);
                 }
             }
         }
@@ -287,9 +294,10 @@
 
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                             base::unique_fd(), nullptr);
+                             base::unique_fd(), &drawFence);
 }
 
 static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -316,9 +324,10 @@
 
     };
 
+    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                            base::unique_fd(), nullptr);
+                            base::unique_fd(), &drawFence);
 }
 
 //
@@ -381,6 +390,7 @@
                                                           ExternalTexture::Usage::WRITEABLE);
         drawHolePunchLayer(renderengine, display, dstTexture);
         drawSolidLayers(renderengine, display, dstTexture);
+
         drawShadowLayers(renderengine, display, srcTexture);
         drawShadowLayers(renderengine, p3Display, srcTexture);
 
@@ -421,6 +431,14 @@
 
         drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
 
+        // draw one final layer synchronously to force GL submit
+        LayerSettings layer{
+                .source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)},
+        };
+        auto layers = std::vector<const LayerSettings*>{&layer};
+        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                             base::unique_fd(), nullptr); // null drawFence makes it synchronous
+
         const nsecs_t timeAfter = systemTime();
         const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
         const int shadersCompiled = renderengine->reportShadersCompiled();
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index f4b07d0..9fbbdc3 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -812,28 +812,6 @@
     canvas->clear(SK_ColorTRANSPARENT);
     initCanvas(canvas, display);
 
-    // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
-    // view is still on-screen. The clear region could be re-specified as a black color layer,
-    // however.
-    if (!display.clearRegion.isEmpty()) {
-        ATRACE_NAME("ClearRegion");
-        size_t numRects = 0;
-        Rect const* rects = display.clearRegion.getArray(&numRects);
-        SkIRect skRects[numRects];
-        for (int i = 0; i < numRects; ++i) {
-            skRects[i] =
-                    SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
-        }
-        SkRegion clearRegion;
-        SkPaint paint;
-        sk_sp<SkShader> shader =
-                SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
-                                 toSkColorSpace(dstDataspace));
-        paint.setShader(shader);
-        clearRegion.setRects(skRects, numRects);
-        canvas->drawRegion(clearRegion, paint);
-    }
-
     for (const auto& layer : layers) {
         ATRACE_FORMAT("DrawLayer: %s", layer->name.c_str());
 
@@ -1149,6 +1127,73 @@
     return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
 }
 
+/**
+ *  Verifies that common, simple bounds + clip combinations can be converted into
+ *  a single RRect draw call returning true if possible. If true the radii parameter
+ *  will be filled with the correct radii values that combined with bounds param will
+ *  produce the insected roundRect. If false, the returned state of the radii param is undefined.
+ */
+static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
+                                    const SkRect& insetCrop, float cornerRadius,
+                                    SkVector radii[4]) {
+    const bool leftEqual = bounds.fLeft == crop.fLeft;
+    const bool topEqual = bounds.fTop == crop.fTop;
+    const bool rightEqual = bounds.fRight == crop.fRight;
+    const bool bottomEqual = bounds.fBottom == crop.fBottom;
+
+    // In the event that the corners of the bounds only partially align with the crop we
+    // need to ensure that the resulting shape can still be represented as a round rect.
+    // In particular the round rect implementation will scale the value of all corner radii
+    // if the sum of the radius along any edge is greater than the length of that edge.
+    // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
+    const bool requiredWidth = bounds.width() > (cornerRadius * 2);
+    const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+    if (!requiredWidth || !requiredHeight) {
+        return false;
+    }
+
+    // Check each cropped corner to ensure that it exactly matches the crop or its corner is
+    // contained within the cropped shape and does not need rounded.
+    // compute the UpperLeft corner radius
+    if (leftEqual && topEqual) {
+        radii[0].set(cornerRadius, cornerRadius);
+    } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
+               (topEqual && bounds.fLeft >= insetCrop.fLeft)) {
+        radii[0].set(0, 0);
+    } else {
+        return false;
+    }
+    // compute the UpperRight corner radius
+    if (rightEqual && topEqual) {
+        radii[1].set(cornerRadius, cornerRadius);
+    } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
+               (topEqual && bounds.fRight <= insetCrop.fRight)) {
+        radii[1].set(0, 0);
+    } else {
+        return false;
+    }
+    // compute the BottomRight corner radius
+    if (rightEqual && bottomEqual) {
+        radii[2].set(cornerRadius, cornerRadius);
+    } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
+               (bottomEqual && bounds.fRight <= insetCrop.fRight)) {
+        radii[2].set(0, 0);
+    } else {
+        return false;
+    }
+    // compute the BottomLeft corner radius
+    if (leftEqual && bottomEqual) {
+        radii[3].set(cornerRadius, cornerRadius);
+    } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
+               (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
+        radii[3].set(0, 0);
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
 inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
                                                                         const FloatRect& cropRect,
                                                                         const float cornerRadius) {
@@ -1166,66 +1211,20 @@
         // converting them to a single RRect draw. It is possible there are other cases
         // that can be converted.
         if (crop.contains(bounds)) {
-            bool intersectionIsRoundRect = true;
-            // check each cropped corner to ensure that it exactly matches the crop or is full
-            SkVector radii[4];
-
             const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
-
-            const bool leftEqual = bounds.fLeft == crop.fLeft;
-            const bool topEqual = bounds.fTop == crop.fTop;
-            const bool rightEqual = bounds.fRight == crop.fRight;
-            const bool bottomEqual = bounds.fBottom == crop.fBottom;
-
-            // compute the UpperLeft corner radius
-            if (leftEqual && topEqual) {
-                radii[0].set(cornerRadius, cornerRadius);
-            } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
-                       (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
-                       insetCrop.contains(bounds.fLeft, bounds.fTop)) {
-                radii[0].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
-            }
-            // compute the UpperRight corner radius
-            if (rightEqual && topEqual) {
-                radii[1].set(cornerRadius, cornerRadius);
-            } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
-                       (topEqual && bounds.fRight <= insetCrop.fRight) ||
-                       insetCrop.contains(bounds.fRight, bounds.fTop)) {
-                radii[1].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
-            }
-            // compute the BottomRight corner radius
-            if (rightEqual && bottomEqual) {
-                radii[2].set(cornerRadius, cornerRadius);
-            } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
-                       (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
-                       insetCrop.contains(bounds.fRight, bounds.fBottom)) {
-                radii[2].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
-            }
-            // compute the BottomLeft corner radius
-            if (leftEqual && bottomEqual) {
-                radii[3].set(cornerRadius, cornerRadius);
-            } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
-                       (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
-                       insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
-                radii[3].set(0, 0);
-            } else {
-                intersectionIsRoundRect = false;
+            if (insetCrop.contains(bounds)) {
+                return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
             }
 
-            if (intersectionIsRoundRect) {
+            SkVector radii[4];
+            if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) {
                 SkRRect intersectionBounds;
                 intersectionBounds.setRectRadii(bounds, radii);
                 return {intersectionBounds, clip};
             }
         }
 
-        // we didn't it any of our fast paths so set the clip to the cropRect
+        // we didn't hit any of our fast paths so set the clip to the cropRect
         clip.setRectXY(crop, cornerRadius, cornerRadius);
     }
 
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 33e3773..33053a0 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -524,10 +524,6 @@
 
     void fillGreenColorBufferThenClearRegion();
 
-    void clearLeftRegion();
-
-    void clearRegion();
-
     template <typename SourceVariant>
     void drawShadow(const renderengine::LayerSettings& castingLayer,
                     const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
@@ -1195,28 +1191,6 @@
     expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
 }
 
-void RenderEngineTest::clearLeftRegion() {
-    renderengine::DisplaySettings settings;
-    settings.physicalDisplay = fullscreenRect();
-    // Here logical space is 4x4
-    settings.clip = Rect(4, 4);
-    settings.clearRegion = Region(Rect(2, 4));
-    std::vector<const renderengine::LayerSettings*> layers;
-    // fake layer, without bounds should not render anything
-    renderengine::LayerSettings layer;
-    layers.push_back(&layer);
-    invokeDraw(settings, layers);
-}
-
-void RenderEngineTest::clearRegion() {
-    // Reuse mBuffer
-    clearLeftRegion();
-    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
-    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
-                           DEFAULT_DISPLAY_HEIGHT),
-                      0, 0, 0, 0);
-}
-
 template <typename SourceVariant>
 void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
                                   const renderengine::ShadowSettings& shadow,
@@ -1647,11 +1621,6 @@
     fillBufferWithoutPremultiplyAlpha();
 }
 
-TEST_P(RenderEngineTest, drawLayers_clearRegion) {
-    initializeRenderEngine();
-    clearRegion();
-}
-
 TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
     initializeRenderEngine();
 
@@ -1900,6 +1869,40 @@
     expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
 }
 
+TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) {
+    initializeRenderEngine();
+
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    renderengine::LayerSettings redLayer;
+    redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
+    redLayer.geometry.roundedCornersRadius = 64;
+    redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
+    // Red background.
+    redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+    redLayer.alpha = 1.0f;
+
+    layers.push_back(&redLayer);
+    invokeDraw(settings, layers);
+
+    // Due to roundedCornersRadius, the top corners are untouched.
+    expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+
+    // ensure that the entire height of the red layer was clipped by the rounded corners crop.
+    expectBufferColor(Point(0, 31), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 31), 0, 0, 0, 0);
+
+    // the bottom middle should be red
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
+}
+
 TEST_P(RenderEngineTest, testClear) {
     initializeRenderEngine();
 
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index eed58c5..ba5a64f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -137,7 +137,6 @@
         "HdrCapabilities.cpp",
         "PixelFormat.cpp",
         "PublicFormat.cpp",
-        "Size.cpp",
         "StaticDisplayInfo.cpp",
     ],
 
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index f196ab9..9120972 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -38,12 +38,22 @@
 
     uint64_t value;
 
+    // For deserialization.
+    static constexpr std::optional<DisplayId> fromValue(uint64_t);
+
+    // As above, but also upcast to Id.
+    template <typename Id>
+    static constexpr std::optional<Id> fromValue(uint64_t value) {
+        if (const auto id = Id::tryCast(DisplayId(value))) {
+            return id;
+        }
+        return {};
+    }
+
 protected:
     explicit constexpr DisplayId(uint64_t id) : value(id) {}
 };
 
-static_assert(sizeof(DisplayId) == sizeof(uint64_t));
-
 inline bool operator==(DisplayId lhs, DisplayId rhs) {
     return lhs.value == rhs.value;
 }
@@ -80,11 +90,8 @@
 
     // TODO(b/162612135) Remove default constructor
     PhysicalDisplayId() = default;
-    // TODO(b/162612135) Remove constructor
-    explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
 
     constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
-
     constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
 
 private:
@@ -96,10 +103,9 @@
     explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}
 };
 
-static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
-
 struct VirtualDisplayId : DisplayId {
     using BaseId = uint32_t;
+
     // Flag indicating that this virtual display is backed by the GPU.
     static constexpr uint64_t FLAG_GPU = 1ULL << 61;
 
@@ -163,10 +169,23 @@
     explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
 };
 
+constexpr std::optional<DisplayId> DisplayId::fromValue(uint64_t value) {
+    if (const auto id = fromValue<PhysicalDisplayId>(value)) {
+        return id;
+    }
+    if (const auto id = fromValue<VirtualDisplayId>(value)) {
+        return id;
+    }
+    return {};
+}
+
+static_assert(sizeof(DisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
 static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
+
+static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
 static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));
 static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t));
-static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
 
 } // namespace android
 
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
index 70a0d50..98ee356 100644
--- a/libs/ui/include/ui/DisplayState.h
+++ b/libs/ui/include/ui/DisplayState.h
@@ -16,21 +16,18 @@
 
 #pragma once
 
+#include <ui/LayerStack.h>
 #include <ui/Rotation.h>
 #include <ui/Size.h>
 
-#include <cstdint>
 #include <type_traits>
 
 namespace android::ui {
 
-using LayerStack = uint32_t;
-constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1);
-
 // Transactional state of physical or virtual display. Note that libgui defines
 // android::DisplayState as a superset of android::ui::DisplayState.
 struct DisplayState {
-    LayerStack layerStack = NO_LAYER_STACK;
+    LayerStack layerStack;
     Rotation orientation = ROTATION_0;
     Size layerStackSpaceRect;
 };
diff --git a/libs/ui/include/ui/LayerStack.h b/libs/ui/include/ui/LayerStack.h
new file mode 100644
index 0000000..d6ffeb7
--- /dev/null
+++ b/libs/ui/include/ui/LayerStack.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <ftl/cast.h>
+#include <ftl/string.h>
+#include <log/log.h>
+
+namespace android::ui {
+
+// A LayerStack identifies a Z-ordered group of layers. A layer can only be associated to a single
+// LayerStack, but a LayerStack can be associated to multiple displays, mirroring the same content.
+struct LayerStack {
+    uint32_t id = UINT32_MAX;
+
+    template <typename T>
+    static constexpr LayerStack fromValue(T v) {
+        if (ftl::cast_safety<uint32_t>(v) == ftl::CastSafety::kSafe) {
+            return {static_cast<uint32_t>(v)};
+        }
+
+        ALOGW("Invalid layer stack %s", ftl::to_string(v).c_str());
+        return {};
+    }
+};
+
+constexpr LayerStack INVALID_LAYER_STACK;
+constexpr LayerStack DEFAULT_LAYER_STACK{0u};
+
+inline bool operator==(LayerStack lhs, LayerStack rhs) {
+    return lhs.id == rhs.id;
+}
+
+inline bool operator!=(LayerStack lhs, LayerStack rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator>(LayerStack lhs, LayerStack rhs) {
+    return lhs.id > rhs.id;
+}
+
+// A LayerFilter determines if a layer is included for output to a display.
+struct LayerFilter {
+    LayerStack layerStack;
+
+    // True if the layer is only output to internal displays, i.e. excluded from screenshots, screen
+    // recordings, and mirroring to virtual or external displays. Used for display cutout overlays.
+    bool toInternalDisplay = false;
+
+    // Returns true if the input filter can be output to this filter.
+    bool includes(LayerFilter other) const {
+        // The layer stacks must match.
+        if (other.layerStack == INVALID_LAYER_STACK || other.layerStack != layerStack) {
+            return false;
+        }
+
+        // The output must be to an internal display if the input filter has that constraint.
+        return !other.toInternalDisplay || toInternalDisplay;
+    }
+};
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index f1e8252..ecc192d 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -23,103 +23,70 @@
 #include <type_traits>
 #include <utility>
 
-namespace android {
-namespace ui {
+namespace android::ui {
 
-// Forward declare a few things.
-struct Size;
-bool operator==(const Size& lhs, const Size& rhs);
-
-/**
- * A simple value type representing a two-dimensional size
- */
+// A simple value type representing a two-dimensional size.
 struct Size {
-    int32_t width;
-    int32_t height;
+    int32_t width = -1;
+    int32_t height = -1;
 
-    // Special values
-    static const Size INVALID;
-    static const Size EMPTY;
+    constexpr Size() = default;
 
-    // ------------------------------------------------------------------------
-    // Construction
-    // ------------------------------------------------------------------------
-
-    Size() : Size(INVALID) {}
     template <typename T>
-    Size(T&& w, T&& h)
-          : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
-            height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
-
-    // ------------------------------------------------------------------------
-    // Accessors
-    // ------------------------------------------------------------------------
+    constexpr Size(T w, T h) : width(clamp<int32_t>(w)), height(clamp<int32_t>(h)) {}
 
     int32_t getWidth() const { return width; }
     int32_t getHeight() const { return height; }
 
-    template <typename T>
-    void setWidth(T&& v) {
-        width = Size::clamp<int32_t, T>(std::forward<T>(v));
-    }
-    template <typename T>
-    void setHeight(T&& v) {
-        height = Size::clamp<int32_t, T>(std::forward<T>(v));
-    }
-
-    // ------------------------------------------------------------------------
-    // Assignment
-    // ------------------------------------------------------------------------
-
-    void set(const Size& size) { *this = size; }
-    template <typename T>
-    void set(T&& w, T&& h) {
-        set(Size(std::forward<T>(w), std::forward<T>(h)));
-    }
-
-    // Sets the value to INVALID
-    void makeInvalid() { set(INVALID); }
-
-    // Sets the value to EMPTY
-    void clear() { set(EMPTY); }
-
-    // ------------------------------------------------------------------------
-    // Semantic checks
-    // ------------------------------------------------------------------------
-
     // Valid means non-negative width and height
     bool isValid() const { return width >= 0 && height >= 0; }
 
     // Empty means zero width and height
-    bool isEmpty() const { return *this == EMPTY; }
+    bool isEmpty() const;
 
-    // ------------------------------------------------------------------------
-    // Clamp Helpers
-    // ------------------------------------------------------------------------
-
-    // Note: We use only features available in C++11 here for compatibility with
-    // external targets which include this file directly or indirectly and which
-    // themselves use C++11.
-
-    // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
     template <typename T>
-    using remove_cv_reference_t =
-            typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+    void setWidth(T v) {
+        width = clamp<int32_t>(v);
+    }
+
+    template <typename T>
+    void setHeight(T v) {
+        height = clamp<int32_t>(v);
+    }
+
+    void set(Size size) { *this = size; }
+
+    template <typename T>
+    void set(T w, T h) {
+        set(Size(w, h));
+    }
+
+    // Sets the value to kInvalidSize
+    void makeInvalid();
+
+    // Sets the value to kEmptySize
+    void clear();
+
+    // TODO: Replace with std::remove_cvref_t in C++20.
+    template <typename T>
+    using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
 
     // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
     // clamping the input value to the output range if necessary.
     template <typename ToType, typename FromType>
-    static Size::remove_cv_reference_t<ToType>
-    clamp(typename std::enable_if<
-            std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
-                    std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
-            FromType>::type v) {
-        using BareToType = remove_cv_reference_t<ToType>;
-        using BareFromType = remove_cv_reference_t<FromType>;
-        static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
-        static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
-        static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
-        static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
+    static constexpr remove_cvref_t<ToType> clamp(FromType v) {
+        using BareToType = remove_cvref_t<ToType>;
+        using ToLimits = std::numeric_limits<BareToType>;
+
+        using BareFromType = remove_cvref_t<FromType>;
+        using FromLimits = std::numeric_limits<BareFromType>;
+
+        static_assert(ToLimits::is_specialized && FromLimits::is_specialized);
+
+        constexpr auto toHighest = ToLimits::max();
+        constexpr auto toLowest = ToLimits::lowest();
+        constexpr auto fromHighest = FromLimits::max();
+        constexpr auto fromLowest = FromLimits::lowest();
 
         // Get the closest representation of [toLowest, toHighest] in type
         // FromType to use to clamp the input value before conversion.
@@ -127,37 +94,35 @@
         // std::common_type<...> is used to get a value-preserving type for the
         // top end of the range.
         using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+        using CommonLimits = std::numeric_limits<CommonHighestType>;
 
         // std::make_signed<std::common_type<...>> is used to get a
         // value-preserving type for the bottom end of the range, except this is
         // a bit trickier for non-integer types like float.
-        using CommonLowestType =
-                std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
-                                   std::make_signed_t<std::conditional_t<
-                                           std::numeric_limits<CommonHighestType>::is_integer,
-                                           CommonHighestType, int /* not used */>>,
-                                   CommonHighestType>;
+        using CommonLowestType = std::conditional_t<
+                CommonLimits::is_integer,
+                std::make_signed_t<std::conditional_t<CommonLimits::is_integer, CommonHighestType,
+                                                      int /* not used */>>,
+                CommonHighestType>;
 
         // We can then compute the clamp range in a way that can be later
         // trivially converted to either the 'from' or 'to' types, and be
-        // representabile in either.
-        static constexpr auto commonClampHighest =
-                std::min(static_cast<CommonHighestType>(fromHighest),
-                         static_cast<CommonHighestType>(toHighest));
-        static constexpr auto commonClampLowest =
-                std::max(static_cast<CommonLowestType>(fromLowest),
-                         static_cast<CommonLowestType>(toLowest));
+        // representable in either.
+        constexpr auto commonClampHighest = std::min(static_cast<CommonHighestType>(fromHighest),
+                                                     static_cast<CommonHighestType>(toHighest));
+        constexpr auto commonClampLowest = std::max(static_cast<CommonLowestType>(fromLowest),
+                                                    static_cast<CommonLowestType>(toLowest));
 
-        static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
-        static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+        constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+        constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
 
         // A clamp is needed only if the range we are clamping to is not the
         // same as the range of the input.
-        static constexpr bool isClampNeeded =
+        constexpr bool isClampNeeded =
                 (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
 
         // If a clamp is not needed, the conversion is just a trivial cast.
-        if (!isClampNeeded) {
+        if constexpr (!isClampNeeded) {
             return static_cast<BareToType>(v);
         }
 
@@ -170,34 +135,46 @@
 
         // Otherwise clamping is done by using the already computed endpoints
         // for each type.
-        return (v <= fromClampLowest)
-                ? toClampLowest
-                : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
+        if (v <= fromClampLowest) {
+            return toClampLowest;
+        }
+
+        return v >= fromClampHighest ? toClampHighest : static_cast<BareToType>(v);
     }
 };
 
-// ------------------------------------------------------------------------
-// Comparisons
-// ------------------------------------------------------------------------
+constexpr Size kInvalidSize;
+constexpr Size kEmptySize{0, 0};
 
-inline bool operator==(const Size& lhs, const Size& rhs) {
+inline void Size::makeInvalid() {
+    set(kInvalidSize);
+}
+
+inline void Size::clear() {
+    set(kEmptySize);
+}
+
+inline bool operator==(Size lhs, Size rhs) {
     return lhs.width == rhs.width && lhs.height == rhs.height;
 }
 
-inline bool operator!=(const Size& lhs, const Size& rhs) {
-    return !operator==(lhs, rhs);
+inline bool Size::isEmpty() const {
+    return *this == kEmptySize;
 }
 
-inline bool operator<(const Size& lhs, const Size& rhs) {
+inline bool operator!=(Size lhs, Size rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator<(Size lhs, Size rhs) {
     // Orders by increasing width, then height.
     if (lhs.width != rhs.width) return lhs.width < rhs.width;
     return lhs.height < rhs.height;
 }
 
 // Defining PrintTo helps with Google Tests.
-static inline void PrintTo(const Size& size, ::std::ostream* os) {
-    *os << "Size(" << size.width << ", " << size.height << ")";
+inline void PrintTo(Size size, std::ostream* stream) {
+    *stream << "Size(" << size.width << ", " << size.height << ')';
 }
 
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
index 1d908b8..8ddee7e 100644
--- a/libs/ui/tests/DisplayId_test.cpp
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -32,6 +32,9 @@
     EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
     EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
     EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+    EXPECT_EQ(id, DisplayId::fromValue(id.value));
+    EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
 }
 
 TEST(DisplayIdTest, createPhysicalIdFromPort) {
@@ -43,6 +46,9 @@
     EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
     EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
     EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+    EXPECT_EQ(id, DisplayId::fromValue(id.value));
+    EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
 }
 
 TEST(DisplayIdTest, createGpuVirtualId) {
@@ -52,6 +58,9 @@
     EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
     EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
     EXPECT_FALSE(HalDisplayId::tryCast(id));
+
+    EXPECT_EQ(id, DisplayId::fromValue(id.value));
+    EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
 }
 
 TEST(DisplayIdTest, createHalVirtualId) {
@@ -61,6 +70,9 @@
     EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
     EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
     EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+    EXPECT_EQ(id, DisplayId::fromValue(id.value));
+    EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value));
 }
 
 } // namespace android::ui
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 5f75aea..acef47f 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -93,9 +93,8 @@
     }
 
     {
-        const auto& s = Size::INVALID;
-        EXPECT_FALSE(s.isValid());
-        EXPECT_FALSE(s.isEmpty());
+        EXPECT_FALSE(kInvalidSize.isValid());
+        EXPECT_FALSE(kInvalidSize.isEmpty());
     }
 
     {
@@ -112,9 +111,8 @@
     }
 
     {
-        const auto& s = Size::EMPTY;
-        EXPECT_TRUE(s.isValid());
-        EXPECT_TRUE(s.isEmpty());
+        EXPECT_TRUE(kEmptySize.isValid());
+        EXPECT_TRUE(kEmptySize.isEmpty());
     }
 
     {
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index d6fc695..d205231 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -34,7 +34,10 @@
     sp<IBinder> displayToken = nullptr;
     sp<SurfaceControl> surfaceControl = nullptr;
     if (it == mDisplays.end()) {
-        displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
+        if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
+            displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+        }
+
         if (displayToken == nullptr) {
             ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
             return nullptr;
@@ -157,7 +160,11 @@
     HwDisplayConfig activeConfig;
     HwDisplayState  activeState;
 
-    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
+    sp<IBinder> displayToken;
+    if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
+        displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+    }
+
     if (displayToken == nullptr) {
         ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
     } else {
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index b5ed064..288068f 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -176,10 +176,11 @@
     }
     return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64
                         ", source=0x%08x, displayId=%" PRId32 ", action=%s, "
-                        "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
+                        "flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, "
                         "repeatCount=%d), policyFlags=0x%08x",
                         deviceId, eventTime, source, displayId, KeyEvent::actionToString(action),
-                        flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
+                        flags, KeyEvent::getLabel(keyCode), keyCode, scanCode, metaState,
+                        repeatCount, policyFlags);
 }
 
 void KeyEntry::recycle() {
@@ -318,17 +319,4 @@
     return seq;
 }
 
-// --- CommandEntry ---
-
-CommandEntry::CommandEntry(Command command)
-      : command(command),
-        eventTime(0),
-        keyEntry(nullptr),
-        userActivityEventType(0),
-        seq(0),
-        handled(false),
-        enabled(false) {}
-
-CommandEntry::~CommandEntry() {}
-
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 1b7fcf2..8feb982 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -245,55 +245,6 @@
 VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry);
 VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry);
 
-class InputDispatcher;
-// A command entry captures state and behavior for an action to be performed in the
-// dispatch loop after the initial processing has taken place.  It is essentially
-// a kind of continuation used to postpone sensitive policy interactions to a point
-// in the dispatch loop where it is safe to release the lock (generally after finishing
-// the critical parts of the dispatch cycle).
-//
-// The special thing about commands is that they can voluntarily release and reacquire
-// the dispatcher lock at will.  Initially when the command starts running, the
-// dispatcher lock is held.  However, if the command needs to call into the policy to
-// do some work, it can release the lock, do the work, then reacquire the lock again
-// before returning.
-//
-// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
-// never calls into the policy while holding its lock.
-//
-// Commands are implicitly 'LockedInterruptible'.
-struct CommandEntry;
-typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
-
-class Connection;
-struct CommandEntry {
-    explicit CommandEntry(Command command);
-    ~CommandEntry();
-
-    Command command;
-
-    // parameters for the command (usage varies by command)
-    sp<Connection> connection;
-    nsecs_t eventTime;
-    std::shared_ptr<KeyEntry> keyEntry;
-    std::shared_ptr<SensorEntry> sensorEntry;
-    std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
-    std::string reason;
-    int32_t userActivityEventType;
-    uint32_t seq;
-    bool handled;
-    sp<IBinder> connectionToken;
-    sp<IBinder> oldToken;
-    sp<IBinder> newToken;
-    std::string obscuringPackage;
-    bool enabled;
-    int32_t pid;
-    nsecs_t consumeTime; // time when the event was consumed by InputConsumer
-    int32_t displayId;
-    float x;
-    float y;
-};
-
 } // namespace android::inputdispatcher
 
 #endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3dea9ce..59cb419 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -19,34 +19,6 @@
 
 #define LOG_NDEBUG 1
 
-// Log detailed debug messages about each inbound event notification to the dispatcher.
-#define DEBUG_INBOUND_EVENT_DETAILS 0
-
-// Log detailed debug messages about each outbound event processed by the dispatcher.
-#define DEBUG_OUTBOUND_EVENT_DETAILS 0
-
-// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 0
-
-// Log debug messages about channel creation
-#define DEBUG_CHANNEL_CREATION 0
-
-// Log debug messages about input event injection.
-#define DEBUG_INJECTION 0
-
-// Log debug messages about input focus tracking.
-static constexpr bool DEBUG_FOCUS = false;
-
-// Log debug messages about touch occlusion
-// STOPSHIP(b/169067926): Set to false
-static constexpr bool DEBUG_TOUCH_OCCLUSION = true;
-
-// Log debug messages about the app switch latency optimization.
-#define DEBUG_APP_SWITCH 0
-
-// Log debug messages about hover events.
-#define DEBUG_HOVER 0
-
 #include <InputFlingerProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/properties.h>
@@ -94,9 +66,50 @@
 
 namespace android::inputdispatcher {
 
+namespace {
+
+// Log detailed debug messages about each inbound event notification to the dispatcher.
+constexpr bool DEBUG_INBOUND_EVENT_DETAILS = false;
+
+// Log detailed debug messages about each outbound event processed by the dispatcher.
+constexpr bool DEBUG_OUTBOUND_EVENT_DETAILS = false;
+
+// Log debug messages about the dispatch cycle.
+constexpr bool DEBUG_DISPATCH_CYCLE = false;
+
+// Log debug messages about channel creation
+constexpr bool DEBUG_CHANNEL_CREATION = false;
+
+// Log debug messages about input event injection.
+constexpr bool DEBUG_INJECTION = false;
+
+// Log debug messages about input focus tracking.
+constexpr bool DEBUG_FOCUS = false;
+
+// Log debug messages about touch occlusion
+// STOPSHIP(b/169067926): Set to false
+constexpr bool DEBUG_TOUCH_OCCLUSION = true;
+
+// Log debug messages about the app switch latency optimization.
+constexpr bool DEBUG_APP_SWITCH = false;
+
+// Log debug messages about hover events.
+constexpr bool DEBUG_HOVER = false;
+
+// Temporarily releases a held mutex for the lifetime of the instance.
+// Named to match std::scoped_lock
+class scoped_unlock {
+public:
+    explicit scoped_unlock(std::mutex& mutex) : mMutex(mutex) { mMutex.unlock(); }
+    ~scoped_unlock() { mMutex.lock(); }
+
+private:
+    std::mutex& mMutex;
+};
+
 // When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
 // coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-static bool isPerWindowInputRotationEnabled() {
+bool isPerWindowInputRotationEnabled() {
     static const bool PER_WINDOW_INPUT_ROTATION =
             sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
 
@@ -136,27 +149,27 @@
 constexpr int LOGTAG_INPUT_INTERACTION = 62000;
 constexpr int LOGTAG_INPUT_FOCUS = 62001;
 
-static inline nsecs_t now() {
+inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
-static inline const char* toString(bool value) {
+inline const char* toString(bool value) {
     return value ? "true" : "false";
 }
 
-static inline const std::string toString(sp<IBinder> binder) {
+inline const std::string toString(const sp<IBinder>& binder) {
     if (binder == nullptr) {
         return "<null>";
     }
     return StringPrintf("%p", binder.get());
 }
 
-static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
+inline int32_t getMotionEventActionPointerIndex(int32_t action) {
     return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
             AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
 }
 
-static bool isValidKeyAction(int32_t action) {
+bool isValidKeyAction(int32_t action) {
     switch (action) {
         case AKEY_EVENT_ACTION_DOWN:
         case AKEY_EVENT_ACTION_UP:
@@ -166,7 +179,7 @@
     }
 }
 
-static bool validateKeyEvent(int32_t action) {
+bool validateKeyEvent(int32_t action) {
     if (!isValidKeyAction(action)) {
         ALOGE("Key event has invalid action code 0x%x", action);
         return false;
@@ -174,7 +187,7 @@
     return true;
 }
 
-static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
+bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
     switch (action & AMOTION_EVENT_ACTION_MASK) {
         case AMOTION_EVENT_ACTION_DOWN:
         case AMOTION_EVENT_ACTION_UP:
@@ -199,12 +212,12 @@
     }
 }
 
-static int64_t millis(std::chrono::nanoseconds t) {
+int64_t millis(std::chrono::nanoseconds t) {
     return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
 }
 
-static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
-                                const PointerProperties* pointerProperties) {
+bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
+                         const PointerProperties* pointerProperties) {
     if (!isValidMotionAction(action, actionButton, pointerCount)) {
         ALOGE("Motion event has invalid action code 0x%x", action);
         return false;
@@ -231,7 +244,7 @@
     return true;
 }
 
-static std::string dumpRegion(const Region& region) {
+std::string dumpRegion(const Region& region) {
     if (region.isEmpty()) {
         return "<empty>";
     }
@@ -252,7 +265,7 @@
     return dump;
 }
 
-static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
+std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
     constexpr size_t maxEntries = 50; // max events to print
     constexpr size_t skipBegin = maxEntries / 2;
     const size_t skipEnd = queue.size() - maxEntries / 2;
@@ -291,12 +304,12 @@
  * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned.
  */
 template <typename K, typename V>
-static V getValueByKey(const std::unordered_map<K, V>& map, K key) {
+V getValueByKey(const std::unordered_map<K, V>& map, K key) {
     auto it = map.find(key);
     return it != map.end() ? it->second : V{};
 }
 
-static bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) {
+bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) {
     if (first == second) {
         return true;
     }
@@ -308,7 +321,7 @@
     return first->getToken() == second->getToken();
 }
 
-static bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) {
+bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) {
     if (first == nullptr || second == nullptr) {
         return false;
     }
@@ -316,13 +329,13 @@
             first->applicationInfo.token == second->applicationInfo.token;
 }
 
-static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
     return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
 }
 
-static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
-                                                          std::shared_ptr<EventEntry> eventEntry,
-                                                          int32_t inputTargetFlags) {
+std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+                                                   std::shared_ptr<EventEntry> eventEntry,
+                                                   int32_t inputTargetFlags) {
     if (eventEntry->type == EventEntry::Type::MOTION) {
         const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
         if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) ||
@@ -399,9 +412,9 @@
     return dispatchEntry;
 }
 
-static void addGestureMonitors(const std::vector<Monitor>& monitors,
-                               std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
-                               float yOffset = 0) {
+void addGestureMonitors(const std::vector<Monitor>& monitors,
+                        std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
+                        float yOffset = 0) {
     if (monitors.empty()) {
         return;
     }
@@ -411,9 +424,8 @@
     }
 }
 
-static status_t openInputChannelPair(const std::string& name,
-                                     std::shared_ptr<InputChannel>& serverChannel,
-                                     std::unique_ptr<InputChannel>& clientChannel) {
+status_t openInputChannelPair(const std::string& name, std::shared_ptr<InputChannel>& serverChannel,
+                              std::unique_ptr<InputChannel>& clientChannel) {
     std::unique_ptr<InputChannel> uniqueServerChannel;
     status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel);
 
@@ -422,7 +434,7 @@
 }
 
 template <typename T>
-static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
+bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
     if (lhs == nullptr && rhs == nullptr) {
         return true;
     }
@@ -432,7 +444,7 @@
     return *lhs == *rhs;
 }
 
-static sp<IPlatformCompatNative> getCompatService() {
+sp<IPlatformCompatNative> getCompatService() {
     sp<IBinder> service(defaultServiceManager()->getService(String16("platform_compat_native")));
     if (service == nullptr) {
         ALOGE("Failed to link to compat service");
@@ -441,7 +453,7 @@
     return interface_cast<IPlatformCompatNative>(service);
 }
 
-static KeyEvent createKeyEvent(const KeyEntry& entry) {
+KeyEvent createKeyEvent(const KeyEntry& entry) {
     KeyEvent event;
     event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
                      entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
@@ -449,7 +461,7 @@
     return event;
 }
 
-static std::optional<int32_t> findMonitorPidByToken(
+std::optional<int32_t> findMonitorPidByToken(
         const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay,
         const sp<IBinder>& token) {
     for (const auto& it : monitorsByDisplay) {
@@ -463,7 +475,7 @@
     return std::nullopt;
 }
 
-static bool shouldReportMetricsForConnection(const Connection& connection) {
+bool shouldReportMetricsForConnection(const Connection& connection) {
     // Do not keep track of gesture monitors. They receive every event and would disproportionately
     // affect the statistics.
     if (connection.monitor) {
@@ -476,8 +488,7 @@
     return true;
 }
 
-static bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry,
-                                      const Connection& connection) {
+bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, const Connection& connection) {
     const EventEntry& eventEntry = *dispatchEntry.eventEntry;
     const int32_t& inputEventId = eventEntry.id;
     if (inputEventId != dispatchEntry.resolvedEventId) {
@@ -513,6 +524,22 @@
     return true;
 }
 
+/**
+ * Connection is responsive if it has no events in the waitQueue that are older than the
+ * current time.
+ */
+bool isConnectionResponsive(const Connection& connection) {
+    const nsecs_t currentTime = now();
+    for (const DispatchEntry* entry : connection.waitQueue) {
+        if (entry->timeoutTime < currentTime) {
+            return false;
+        }
+    }
+    return true;
+}
+
+} // namespace
+
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -546,17 +573,17 @@
 }
 
 InputDispatcher::~InputDispatcher() {
-    { // acquire lock
-        std::scoped_lock _l(mLock);
+    std::scoped_lock _l(mLock);
 
-        resetKeyRepeatLocked();
-        releasePendingEventLocked();
-        drainInboundQueueLocked();
-    }
+    resetKeyRepeatLocked();
+    releasePendingEventLocked();
+    drainInboundQueueLocked();
+    mCommandQueue.clear();
 
     while (!mConnectionsByToken.empty()) {
         sp<Connection> connection = mConnectionsByToken.begin()->second;
-        removeInputChannel(connection->inputChannel->getConnectionToken());
+        removeInputChannelLocked(connection->inputChannel->getConnectionToken(),
+                                 false /* notify */);
     }
 }
 
@@ -596,7 +623,7 @@
 
         // Run all pending commands if there are any.
         // If any commands were run then force the next poll to wake up immediately.
-        if (runCommandsLockedInterruptible()) {
+        if (runCommandsLockedInterruptable()) {
             nextWakeupTime = LONG_LONG_MIN;
         }
 
@@ -911,8 +938,7 @@
         }
 
         // Alternatively, maybe there's a gesture monitor that could handle this event
-        std::vector<TouchedMonitor> gestureMonitors =
-                findTouchedGestureMonitorsLocked(displayId, {});
+        std::vector<TouchedMonitor> gestureMonitors = findTouchedGestureMonitorsLocked(displayId);
         for (TouchedMonitor& gestureMonitor : gestureMonitors) {
             sp<Connection> connection =
                     getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
@@ -957,9 +983,9 @@
                     mAppSwitchSawKeyDown = true;
                 } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
                     if (mAppSwitchSawKeyDown) {
-#if DEBUG_APP_SWITCH
-                        ALOGD("App switch is pending!");
-#endif
+                        if (DEBUG_APP_SWITCH) {
+                            ALOGD("App switch is pending!");
+                        }
                         mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
                         mAppSwitchSawKeyDown = false;
                         needWake = true;
@@ -1006,11 +1032,9 @@
 sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                 int32_t y, TouchState* touchState,
                                                                 bool addOutsideTargets,
-                                                                bool addPortalWindows,
                                                                 bool ignoreDragWindow) {
-    if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
-        LOG_ALWAYS_FATAL(
-                "Must provide a valid touch state if adding portal windows or outside targets");
+    if (addOutsideTargets && touchState == nullptr) {
+        LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
     }
     // Traverse windows from front to back to find touched window.
     const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
@@ -1027,16 +1051,6 @@
                     bool isTouchModal = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&
                             !flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL);
                     if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
-                        int32_t portalToDisplayId = windowInfo->portalToDisplayId;
-                        if (portalToDisplayId != ADISPLAY_ID_NONE &&
-                            portalToDisplayId != displayId) {
-                            if (addPortalWindows) {
-                                // For the monitoring channels of the display.
-                                touchState->addPortalWindow(windowHandle);
-                            }
-                            return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
-                                                             addOutsideTargets, addPortalWindows);
-                        }
                         // Found window.
                         return windowHandle;
                     }
@@ -1054,17 +1068,11 @@
 }
 
 std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
-        int32_t displayId, const std::vector<sp<WindowInfoHandle>>& portalWindows) const {
+        int32_t displayId) const {
     std::vector<TouchedMonitor> touchedMonitors;
 
     std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
     addGestureMonitors(monitors, touchedMonitors);
-    for (const sp<WindowInfoHandle>& portalWindow : portalWindows) {
-        const WindowInfo* windowInfo = portalWindow->getInfo();
-        monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId);
-        addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft,
-                           -windowInfo->frameTop);
-    }
     return touchedMonitors;
 }
 
@@ -1072,9 +1080,9 @@
     const char* reason;
     switch (dropReason) {
         case DropReason::POLICY:
-#if DEBUG_INBOUND_EVENT_DETAILS
-            ALOGD("Dropped event because policy consumed it.");
-#endif
+            if (DEBUG_INBOUND_EVENT_DETAILS) {
+                ALOGD("Dropped event because policy consumed it.");
+            }
             reason = "inbound event was dropped because the policy consumed it";
             break;
         case DropReason::DISABLED:
@@ -1158,37 +1166,35 @@
 void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
     mAppSwitchDueTime = LONG_LONG_MAX;
 
-#if DEBUG_APP_SWITCH
-    if (handled) {
-        ALOGD("App switch has arrived.");
-    } else {
-        ALOGD("App switch was abandoned.");
+    if (DEBUG_APP_SWITCH) {
+        if (handled) {
+            ALOGD("App switch has arrived.");
+        } else {
+            ALOGD("App switch was abandoned.");
+        }
     }
-#endif
 }
 
 bool InputDispatcher::haveCommandsLocked() const {
     return !mCommandQueue.empty();
 }
 
-bool InputDispatcher::runCommandsLockedInterruptible() {
+bool InputDispatcher::runCommandsLockedInterruptable() {
     if (mCommandQueue.empty()) {
         return false;
     }
 
     do {
-        std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front());
+        auto command = std::move(mCommandQueue.front());
         mCommandQueue.pop_front();
-        Command command = commandEntry->command;
-        command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible'
-
-        commandEntry->connection.clear();
+        // Commands are run with the lock held, but may release and re-acquire the lock from within.
+        command();
     } while (!mCommandQueue.empty());
     return true;
 }
 
-void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) {
-    mCommandQueue.push_back(std::move(commandEntry));
+void InputDispatcher::postCommandLocked(Command&& command) {
+    mCommandQueue.push_back(command);
 }
 
 void InputDispatcher::drainInboundQueueLocked() {
@@ -1210,9 +1216,9 @@
 void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
     InjectionState* injectionState = entry->injectionState;
     if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
-#if DEBUG_DISPATCH_CYCLE
-        ALOGD("Injected inbound event was dropped.");
-#endif
+        if (DEBUG_DISPATCH_CYCLE) {
+            ALOGD("Injected inbound event was dropped.");
+        }
         setInjectionResult(*entry, InputEventInjectionResult::FAILED);
     }
     if (entry == mNextUnblockedEvent) {
@@ -1247,27 +1253,28 @@
 
 bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
                                                          const ConfigurationChangedEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
+    }
 
     // Reset key repeating in case a keyboard device was added or removed or something.
     resetKeyRepeatLocked();
 
     // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
-    commandEntry->eventTime = entry.eventTime;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyConfigurationChanged(eventTime);
+    };
+    postCommandLocked(std::move(command));
     return true;
 }
 
 bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
                                                 const DeviceResetEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
-          entry.deviceId);
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
+              entry.deviceId);
+    }
 
     // Reset key repeating in case a keyboard device was disabled or enabled.
     if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->deviceId == entry.deviceId) {
@@ -1408,9 +1415,9 @@
         } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
                    mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
             // The key on device 'deviceId' is still down, do not stop key repeat
-#if DEBUG_INBOUND_EVENT_DETAILS
-            ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
-#endif
+            if (DEBUG_INBOUND_EVENT_DETAILS) {
+                ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
+            }
         } else if (!entry->syntheticRepeat) {
             resetKeyRepeatLocked();
         }
@@ -1441,13 +1448,13 @@
     // Give the policy a chance to intercept the key.
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
-            std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-                    &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
             sp<IBinder> focusedWindowToken =
                     mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
-            commandEntry->connectionToken = focusedWindowToken;
-            commandEntry->keyEntry = entry;
-            postCommandLocked(std::move(commandEntry));
+
+            auto command = [this, focusedWindowToken, entry]() REQUIRES(mLock) {
+                doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
+            };
+            postCommandLocked(std::move(command));
             return false; // wait for the command to run
         } else {
             entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
@@ -1489,47 +1496,42 @@
 }
 
 void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
-          "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
-          "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
-          prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
-          entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
-          entry.repeatCount, entry.downTime);
-#endif
-}
-
-void InputDispatcher::doNotifySensorLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    const std::shared_ptr<SensorEntry>& entry = commandEntry->sensorEntry;
-    if (entry->accuracyChanged) {
-        mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
+              "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
+              "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
+              prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
+              entry.policyFlags, entry.action, entry.flags, entry.keyCode, entry.scanCode,
+              entry.metaState, entry.repeatCount, entry.downTime);
     }
-    mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
-                               entry->hwTimestamp, entry->values);
-    mLock.lock();
 }
 
-void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime,
+                                           const std::shared_ptr<SensorEntry>& entry,
                                            DropReason* dropReason, nsecs_t* nextWakeupTime) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
-          "source=0x%x, sensorType=%s",
-          entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
-          NamedEnum::string(entry->sensorType).c_str());
-#endif
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifySensorLockedInterruptible);
-    commandEntry->sensorEntry = entry;
-    postCommandLocked(std::move(commandEntry));
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
+              "source=0x%x, sensorType=%s",
+              entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
+              NamedEnum::string(entry->sensorType).c_str());
+    }
+    auto command = [this, entry]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+
+        if (entry->accuracyChanged) {
+            mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+        }
+        mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
+                                   entry->hwTimestamp, entry->values);
+    };
+    postCommandLocked(std::move(command));
 }
 
 bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
-          NamedEnum::string(sensorType).c_str());
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
+              NamedEnum::string(sensorType).c_str());
+    }
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -1600,23 +1602,6 @@
     // Add monitor channels from event's or focused display.
     addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
 
-    if (isPointerEvent) {
-        std::unordered_map<int32_t, TouchState>::iterator it =
-                mTouchStatesByDisplay.find(entry->displayId);
-        if (it != mTouchStatesByDisplay.end()) {
-            const TouchState& state = it->second;
-            if (!state.portalWindows.empty()) {
-                // The event has gone through these portal windows, so we add monitoring targets of
-                // the corresponding displays as well.
-                for (size_t i = 0; i < state.portalWindows.size(); i++) {
-                    const WindowInfo* windowInfo = state.portalWindows[i]->getInfo();
-                    addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
-                                                     -windowInfo->frameLeft, -windowInfo->frameTop);
-                }
-            }
-        }
-    }
-
     // Dispatch the motion.
     if (conflictingPointerActions) {
         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -1657,42 +1642,43 @@
 }
 
 void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
-          ", policyFlags=0x%x, "
-          "action=0x%x, actionButton=0x%x, flags=0x%x, "
-          "metaState=0x%x, buttonState=0x%x,"
-          "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
-          prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
-          entry.action, entry.actionButton, entry.flags, entry.metaState, entry.buttonState,
-          entry.edgeFlags, entry.xPrecision, entry.yPrecision, entry.downTime);
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+              ", policyFlags=0x%x, "
+              "action=0x%x, actionButton=0x%x, flags=0x%x, "
+              "metaState=0x%x, buttonState=0x%x,"
+              "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
+              prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
+              entry.policyFlags, entry.action, entry.actionButton, entry.flags, entry.metaState,
+              entry.buttonState, entry.edgeFlags, entry.xPrecision, entry.yPrecision,
+              entry.downTime);
 
-    for (uint32_t i = 0; i < entry.pointerCount; i++) {
-        ALOGD("  Pointer %d: id=%d, toolType=%d, "
-              "x=%f, y=%f, pressure=%f, size=%f, "
-              "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
-              "orientation=%f",
-              i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType,
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+        for (uint32_t i = 0; i < entry.pointerCount; i++) {
+            ALOGD("  Pointer %d: id=%d, toolType=%d, "
+                  "x=%f, y=%f, pressure=%f, size=%f, "
+                  "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+                  "orientation=%f",
+                  i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType,
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                  entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+        }
     }
-#endif
 }
 
 void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
                                           std::shared_ptr<EventEntry> eventEntry,
                                           const std::vector<InputTarget>& inputTargets) {
     ATRACE_CALL();
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("dispatchEventToCurrentInputTargets");
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("dispatchEventToCurrentInputTargets");
+    }
 
     updateInteractionTokensLocked(*eventEntry, inputTargets);
 
@@ -2004,12 +1990,11 @@
             y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
         }
         bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        newTouchedWindowHandle =
-                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
-                                          isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
+        newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
+                                                           isDown /*addOutsideTargets*/);
 
         std::vector<TouchedMonitor> newGestureMonitors = isDown
-                ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)
+                ? findTouchedGestureMonitorsLocked(displayId)
                 : std::vector<TouchedMonitor>{};
 
         // Figure out whether splitting will be allowed for this window.
@@ -2057,7 +2042,7 @@
                         ALOGD("%s", log.c_str());
                     }
                 }
-                onUntrustedTouchLocked(occlusionInfo.obscuringPackage);
+                sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage);
                 if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
                     ALOGW("Dropping untrusted touch event due to %s/%d",
                           occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
@@ -2174,10 +2159,10 @@
         if (mLastHoverWindowHandle != nullptr &&
             (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
              mLastHoverWindowHandle != newTouchedWindowHandle)) {
-#if DEBUG_HOVER
-            ALOGD("Sending hover exit event to window %s.",
-                  mLastHoverWindowHandle->getName().c_str());
-#endif
+            if (DEBUG_HOVER) {
+                ALOGD("Sending hover exit event to window %s.",
+                      mLastHoverWindowHandle->getName().c_str());
+            }
             tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
                                              InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
         }
@@ -2187,10 +2172,10 @@
         if (newHoverWindowHandle != nullptr &&
             (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
              newHoverWindowHandle != newTouchedWindowHandle)) {
-#if DEBUG_HOVER
-            ALOGD("Sending hover enter event to window %s.",
-                  newHoverWindowHandle->getName().c_str());
-#endif
+            if (DEBUG_HOVER) {
+                ALOGD("Sending hover enter event to window %s.",
+                      newHoverWindowHandle->getName().c_str());
+            }
             tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
                                              InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
                                              BitSet32(0));
@@ -2380,13 +2365,12 @@
 void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
     const sp<WindowInfoHandle> dropWindow =
             findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/,
-                                      false /*addOutsideTargets*/, false /*addPortalWindows*/,
-                                      true /*ignoreDragWindow*/);
+                                      false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
     if (dropWindow) {
         vec2 local = dropWindow->getInfo()->transform.transform(x, y);
-        notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y);
+        sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y);
     } else {
-        notifyDropWindowLocked(nullptr, 0, 0);
+        sendDropWindowCommandLocked(nullptr, 0, 0);
     }
     mDragState.reset();
 }
@@ -2415,8 +2399,7 @@
 
         const sp<WindowInfoHandle> hoverWindowHandle =
                 findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
-                                          false /*addOutsideTargets*/, false /*addPortalWindows*/,
-                                          true /*ignoreDragWindow*/);
+                                          false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
         // enqueue drag exit if needed.
         if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
             !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
@@ -2433,7 +2416,7 @@
     } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
         finishDragAndDrop(entry.displayId, x, y);
     } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
-        notifyDropWindowLocked(nullptr, 0, 0);
+        sendDropWindowCommandLocked(nullptr, 0, 0);
         mDragState.reset();
     }
 }
@@ -2721,9 +2704,9 @@
     if (focusedWindowHandle != nullptr) {
         const WindowInfo* info = focusedWindowHandle->getInfo();
         if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
-#if DEBUG_DISPATCH_CYCLE
-            ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
-#endif
+            if (DEBUG_DISPATCH_CYCLE) {
+                ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
+            }
             return;
         }
     }
@@ -2761,12 +2744,12 @@
         }
     }
 
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
-    commandEntry->eventTime = eventEntry.eventTime;
-    commandEntry->userActivityEventType = eventType;
-    commandEntry->displayId = displayId;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]()
+                           REQUIRES(mLock) {
+                               scoped_unlock unlock(mLock);
+                               mPolicy->pokeUserActivity(eventTime, eventType, displayId);
+                           };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
@@ -2779,21 +2762,21 @@
                              connection->getInputChannelName().c_str(), eventEntry->id);
         ATRACE_NAME(message.c_str());
     }
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
-          "globalScaleFactor=%f, pointerIds=0x%x %s",
-          connection->getInputChannelName().c_str(), inputTarget.flags,
-          inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
-          inputTarget.getPointerInfoString().c_str());
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
+              "globalScaleFactor=%f, pointerIds=0x%x %s",
+              connection->getInputChannelName().c_str(), inputTarget.flags,
+              inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+              inputTarget.getPointerInfoString().c_str());
+    }
 
     // Skip this event if the connection status is not normal.
     // We don't want to enqueue additional outbound events if the connection is broken.
     if (connection->status != Connection::STATUS_NORMAL) {
-#if DEBUG_DISPATCH_CYCLE
-        ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
-              connection->getInputChannelName().c_str(), connection->getStatusLabel());
-#endif
+        if (DEBUG_DISPATCH_CYCLE) {
+            ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
+                  connection->getInputChannelName().c_str(), connection->getStatusLabel());
+        }
         return;
     }
 
@@ -2892,10 +2875,11 @@
 
             if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
                                                  dispatchEntry->resolvedFlags)) {
-#if DEBUG_DISPATCH_CYCLE
-                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
-                      connection->getInputChannelName().c_str());
-#endif
+                if (DEBUG_DISPATCH_CYCLE) {
+                    ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key "
+                          "event",
+                          connection->getInputChannelName().c_str());
+                }
                 return; // skip the inconsistent event
             }
             break;
@@ -2925,11 +2909,11 @@
             if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
                 !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
                                                    motionEntry.displayId)) {
-#if DEBUG_DISPATCH_CYCLE
-                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
-                      "event",
-                      connection->getInputChannelName().c_str());
-#endif
+                if (DEBUG_DISPATCH_CYCLE) {
+                    ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover "
+                          "enter event",
+                          connection->getInputChannelName().c_str());
+                }
                 // We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because
                 // this is a one-to-one event conversion.
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
@@ -2945,11 +2929,11 @@
 
             if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
                                                     dispatchEntry->resolvedFlags)) {
-#if DEBUG_DISPATCH_CYCLE
-                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
-                      "event",
-                      connection->getInputChannelName().c_str());
-#endif
+                if (DEBUG_DISPATCH_CYCLE) {
+                    ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
+                          "event",
+                          connection->getInputChannelName().c_str());
+                }
                 return; // skip the inconsistent event
             }
 
@@ -3081,10 +3065,11 @@
         return;
     }
 
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
-    commandEntry->newToken = token;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, token]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->onPointerDownOutsideFocus(token);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -3094,9 +3079,9 @@
                                            connection->getInputChannelName().c_str());
         ATRACE_NAME(message.c_str());
     }
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
+    }
 
     while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
         DispatchEntry* dispatchEntry = connection->outboundQueue.front();
@@ -3234,11 +3219,11 @@
                 } else {
                     // Pipe is full and we are waiting for the app to finish process some events
                     // before sending more events to it.
-#if DEBUG_DISPATCH_CYCLE
-                    ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
-                          "waiting for the application to catch up",
-                          connection->getInputChannelName().c_str());
-#endif
+                    if (DEBUG_DISPATCH_CYCLE) {
+                        ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
+                              "waiting for the application to catch up",
+                              connection->getInputChannelName().c_str());
+                    }
                 }
             } else {
                 ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -3305,10 +3290,10 @@
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection, uint32_t seq,
                                                 bool handled, nsecs_t consumeTime) {
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
-          connection->getInputChannelName().c_str(), seq, toString(handled));
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
+              connection->getInputChannelName().c_str(), seq, toString(handled));
+    }
 
     if (connection->status == Connection::STATUS_BROKEN ||
         connection->status == Connection::STATUS_ZOMBIE) {
@@ -3316,16 +3301,19 @@
     }
 
     // Notify other system components and prepare to start the next dispatch cycle.
-    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime);
+    auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {
+        doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
                                                      const sp<Connection>& connection,
                                                      bool notify) {
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
-          connection->getInputChannelName().c_str(), toString(notify));
-#endif
+    if (DEBUG_DISPATCH_CYCLE) {
+        ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
+              connection->getInputChannelName().c_str(), toString(notify));
+    }
 
     // Clear the dispatch queues.
     drainDispatchQueue(connection->outboundQueue);
@@ -3340,7 +3328,15 @@
 
         if (notify) {
             // Notify other system components.
-            onDispatchCycleBrokenLocked(currentTime, connection);
+            ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
+                  connection->getInputChannelName().c_str());
+
+            auto command = [this, connection]() REQUIRES(mLock) {
+                if (connection->status == Connection::STATUS_ZOMBIE) return;
+                scoped_unlock unlock(mLock);
+                mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
+            };
+            postCommandLocked(std::move(command));
         }
     }
 }
@@ -3407,7 +3403,7 @@
             gotOne = true;
         }
         if (gotOne) {
-            runCommandsLockedInterruptible();
+            runCommandsLockedInterruptable();
             if (status == WOULD_BLOCK) {
                 return 1;
             }
@@ -3484,12 +3480,12 @@
     if (cancelationEvents.empty()) {
         return;
     }
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
-          "with reality: %s, mode=%d.",
-          connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
-          options.mode);
-#endif
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+        ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
+              "with reality: %s, mode=%d.",
+              connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
+              options.mode);
+    }
 
     InputTarget target;
     sp<WindowInfoHandle> windowHandle =
@@ -3553,10 +3549,10 @@
         return;
     }
 
-#if DEBUG_OUTBOUND_EVENT_DETAILS
+    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
         ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
               connection->getInputChannelName().c_str(), downEvents.size());
-#endif
+    }
 
     InputTarget target;
     sp<WindowInfoHandle> windowHandle =
@@ -3699,9 +3695,9 @@
 }
 
 void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
+    }
 
     bool needWake;
     { // acquire lock
@@ -3755,14 +3751,14 @@
 }
 
 void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
-          "policyFlags=0x%x, action=0x%x, "
-          "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
-          args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
-          args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
-          args->downTime);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+              "policyFlags=0x%x, action=0x%x, "
+              "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
+              args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
+              args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
+              args->downTime);
+    }
     if (!validateKeyEvent(args->action)) {
         return;
     }
@@ -3833,33 +3829,33 @@
 }
 
 void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
-          "displayId=%" PRId32 ", policyFlags=0x%x, "
-          "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
-          "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
-          "yCursorPosition=%f, downTime=%" PRId64,
-          args->id, args->eventTime, args->deviceId, args->source, args->displayId,
-          args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
-          args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
-          args->xCursorPosition, args->yCursorPosition, args->downTime);
-    for (uint32_t i = 0; i < args->pointerCount; i++) {
-        ALOGD("  Pointer %d: id=%d, toolType=%d, "
-              "x=%f, y=%f, pressure=%f, size=%f, "
-              "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
-              "orientation=%f",
-              i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-              args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+              "displayId=%" PRId32 ", policyFlags=0x%x, "
+              "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
+              "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
+              "yCursorPosition=%f, downTime=%" PRId64,
+              args->id, args->eventTime, args->deviceId, args->source, args->displayId,
+              args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
+              args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
+              args->xCursorPosition, args->yCursorPosition, args->downTime);
+        for (uint32_t i = 0; i < args->pointerCount; i++) {
+            ALOGD("  Pointer %d: id=%d, toolType=%d, "
+                  "x=%f, y=%f, pressure=%f, size=%f, "
+                  "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+                  "orientation=%f",
+                  i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                  args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+        }
     }
-#endif
     if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
                              args->pointerProperties)) {
         return;
@@ -3912,6 +3908,13 @@
                                               args->downTime, args->pointerCount,
                                               args->pointerProperties, args->pointerCoords, 0, 0);
 
+        if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
+            IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER &&
+            !mInputFilterEnabled) {
+            const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN;
+            mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime);
+        }
+
         needWake = enqueueInboundEventLocked(std::move(newEntry));
         mLock.unlock();
     } // release lock
@@ -3922,12 +3925,12 @@
 }
 
 void InputDispatcher::notifySensor(const NotifySensorArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
-          " sensorType=%s",
-          args->id, args->eventTime, args->deviceId, args->source,
-          NamedEnum::string(args->sensorType).c_str());
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+              " sensorType=%s",
+              args->id, args->eventTime, args->deviceId, args->source,
+              NamedEnum::string(args->sensorType).c_str());
+    }
 
     bool needWake;
     { // acquire lock
@@ -3950,10 +3953,10 @@
 }
 
 void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d,  isOn=%d", args->eventTime,
-          args->deviceId, args->isOn);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d,  isOn=%d", args->eventTime,
+              args->deviceId, args->isOn);
+    }
     mPolicy->notifyVibratorState(args->deviceId, args->isOn);
 }
 
@@ -3962,11 +3965,11 @@
 }
 
 void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
-          "switchMask=0x%08x",
-          args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
+              "switchMask=0x%08x",
+              args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
+    }
 
     uint32_t policyFlags = args->policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
@@ -3974,10 +3977,10 @@
 }
 
 void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
-          args->deviceId);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
+              args->deviceId);
+    }
 
     bool needWake;
     { // acquire lock
@@ -3994,10 +3997,10 @@
 }
 
 void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
-          args->enabled ? "true" : "false");
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
+              args->enabled ? "true" : "false");
+    }
 
     bool needWake;
     { // acquire lock
@@ -4015,11 +4018,11 @@
 InputEventInjectionResult InputDispatcher::injectInputEvent(
         const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
         InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
-          "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
-          event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
-#endif
+    if (DEBUG_INBOUND_EVENT_DETAILS) {
+        ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
+              "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
+              event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
+    }
     nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
 
     policyFlags |= POLICY_FLAG_INJECTED;
@@ -4197,10 +4200,10 @@
 
                 nsecs_t remainingTimeout = endTime - now();
                 if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
-                    ALOGD("injectInputEvent - Timed out waiting for injection result "
-                          "to become available.");
-#endif
+                    if (DEBUG_INJECTION) {
+                        ALOGD("injectInputEvent - Timed out waiting for injection result "
+                              "to become available.");
+                    }
                     injectionResult = InputEventInjectionResult::TIMED_OUT;
                     break;
                 }
@@ -4211,16 +4214,16 @@
             if (injectionResult == InputEventInjectionResult::SUCCEEDED &&
                 syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) {
                 while (injectionState->pendingForegroundDispatches != 0) {
-#if DEBUG_INJECTION
-                    ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
-                          injectionState->pendingForegroundDispatches);
-#endif
+                    if (DEBUG_INJECTION) {
+                        ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
+                              injectionState->pendingForegroundDispatches);
+                    }
                     nsecs_t remainingTimeout = endTime - now();
                     if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
-                        ALOGD("injectInputEvent - Timed out waiting for pending foreground "
-                              "dispatches to finish.");
-#endif
+                        if (DEBUG_INJECTION) {
+                            ALOGD("injectInputEvent - Timed out waiting for pending foreground "
+                                  "dispatches to finish.");
+                        }
                         injectionResult = InputEventInjectionResult::TIMED_OUT;
                         break;
                     }
@@ -4233,10 +4236,10 @@
         injectionState->release();
     } // release lock
 
-#if DEBUG_INJECTION
-    ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
-          injectionResult, injectorPid, injectorUid);
-#endif
+    if (DEBUG_INJECTION) {
+        ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
+              injectionResult, injectorPid, injectorUid);
+    }
 
     return injectionResult;
 }
@@ -4283,11 +4286,11 @@
                                          InputEventInjectionResult injectionResult) {
     InjectionState* injectionState = entry.injectionState;
     if (injectionState) {
-#if DEBUG_INJECTION
-        ALOGD("Setting input event injection result to %d.  "
-              "injectorPid=%d, injectorUid=%d",
-              injectionResult, injectionState->injectorPid, injectionState->injectorUid);
-#endif
+        if (DEBUG_INJECTION) {
+            ALOGD("Setting input event injection result to %d.  "
+                  "injectorPid=%d, injectorUid=%d",
+                  injectionResult, injectionState->injectorPid, injectionState->injectorUid);
+        }
 
         if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
             // Log the outcome since the injector did not wait for the injection result.
@@ -4447,8 +4450,7 @@
     std::vector<sp<WindowInfoHandle>> newHandles;
     for (const sp<WindowInfoHandle>& handle : windowInfoHandles) {
         const WindowInfo* info = handle->getInfo();
-        if ((getInputChannelLocked(handle->getToken()) == nullptr &&
-             info->portalToDisplayId == ADISPLAY_ID_NONE)) {
+        if (getInputChannelLocked(handle->getToken()) == nullptr) {
             const bool noInputChannel =
                     info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
             const bool canReceiveInput = !info->flags.test(WindowInfo::Flag::NOT_TOUCHABLE) ||
@@ -4693,7 +4695,7 @@
 
             // Find new focused window and validate
             sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
-            notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
+            sendFocusChangedCommandLocked(oldFocusedWindowToken, newFocusedWindowToken);
 
             if (newFocusedWindowToken == nullptr) {
                 ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
@@ -5000,14 +5002,6 @@
             } else {
                 dump += INDENT3 "Windows: <none>\n";
             }
-            if (!state.portalWindows.empty()) {
-                dump += INDENT3 "Portal windows:\n";
-                for (size_t i = 0; i < state.portalWindows.size(); i++) {
-                    const sp<WindowInfoHandle> portalWindowHandle = state.portalWindows[i];
-                    dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i,
-                                         portalWindowHandle->getName().c_str());
-                }
-            }
         }
     } else {
         dump += INDENT "TouchStates: <no displays touched>\n";
@@ -5029,7 +5023,7 @@
                     const WindowInfo* windowInfo = windowHandle->getInfo();
 
                     dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
-                                                 "portalToDisplayId=%d, paused=%s, focusable=%s, "
+                                                 "paused=%s, focusable=%s, "
                                                  "hasWallpaper=%s, visible=%s, alpha=%.2f, "
                                                  "flags=%s, type=%s, "
                                                  "frame=[%d,%d][%d,%d], globalScale=%f, "
@@ -5037,8 +5031,7 @@
                                                  "applicationInfo.token=%s, "
                                                  "touchableRegion=",
                                          i, windowInfo->name.c_str(), windowInfo->id,
-                                         windowInfo->displayId, windowInfo->portalToDisplayId,
-                                         toString(windowInfo->paused),
+                                         windowInfo->displayId, toString(windowInfo->paused),
                                          toString(windowInfo->focusable),
                                          toString(windowInfo->hasWallpaper),
                                          toString(windowInfo->visible), windowInfo->alpha,
@@ -5135,6 +5128,12 @@
         dump += INDENT "ReplacedKeys: <empty>\n";
     }
 
+    if (!mCommandQueue.empty()) {
+        dump += StringPrintf(INDENT "CommandQueue: size=%zu\n", mCommandQueue.size());
+    } else {
+        dump += INDENT "CommandQueue: <empty>\n";
+    }
+
     if (!mConnectionsByToken.empty()) {
         dump += INDENT "Connections:\n";
         for (const auto& [token, connection] : mConnectionsByToken) {
@@ -5201,9 +5200,9 @@
 };
 
 Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
-#if DEBUG_CHANNEL_CREATION
-    ALOGD("channel '%s' ~ createInputChannel", name.c_str());
-#endif
+    if (DEBUG_CHANNEL_CREATION) {
+        ALOGD("channel '%s' ~ createInputChannel", name.c_str());
+    }
 
     std::unique_ptr<InputChannel> serverChannel;
     std::unique_ptr<InputChannel> clientChannel;
@@ -5484,46 +5483,92 @@
     mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
 }
 
-void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
-                                                    const sp<Connection>& connection, uint32_t seq,
-                                                    bool handled, nsecs_t consumeTime) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
-    commandEntry->connection = connection;
-    commandEntry->eventTime = currentTime;
-    commandEntry->seq = seq;
-    commandEntry->handled = handled;
-    commandEntry->consumeTime = consumeTime;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
+                                                     const sp<Connection>& connection, uint32_t seq,
+                                                     bool handled, nsecs_t consumeTime) {
+    // Handle post-event policy actions.
+    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt == connection->waitQueue.end()) {
+        return;
+    }
+    DispatchEntry* dispatchEntry = *dispatchEntryIt;
+    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
+        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
+    }
+    if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
+        mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
+                                           connection->inputChannel->getConnectionToken(),
+                                           dispatchEntry->deliveryTime, consumeTime, finishTime);
+    }
+
+    bool restartEvent;
+    if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
+        KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
+        restartEvent =
+                afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
+    } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
+        MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
+        restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, motionEntry,
+                                                           handled);
+    } else {
+        restartEvent = false;
+    }
+
+    // Dequeue the event and start the next cycle.
+    // Because the lock might have been released, it is possible that the
+    // contents of the wait queue to have been drained, so we need to double-check
+    // a few things.
+    dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt != connection->waitQueue.end()) {
+        dispatchEntry = *dispatchEntryIt;
+        connection->waitQueue.erase(dispatchEntryIt);
+        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
+        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
+        if (!connection->responsive) {
+            connection->responsive = isConnectionResponsive(*connection);
+            if (connection->responsive) {
+                // The connection was unresponsive, and now it's responsive.
+                processConnectionResponsiveLocked(*connection);
+            }
+        }
+        traceWaitQueueLength(*connection);
+        if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
+            connection->outboundQueue.push_front(dispatchEntry);
+            traceOutboundQueueLength(*connection);
+        } else {
+            releaseDispatchEntry(dispatchEntry);
+        }
+    }
+
+    // Start the next dispatch cycle for this connection.
+    startDispatchCycleLocked(now(), connection);
 }
 
-void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime,
-                                                  const sp<Connection>& connection) {
-    ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
-          connection->getInputChannelName().c_str());
-
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
-    commandEntry->connection = connection;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendFocusChangedCommandLocked(const sp<IBinder>& oldToken,
+                                                    const sp<IBinder>& newToken) {
+    auto command = [this, oldToken, newToken]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyFocusChanged(oldToken, newToken);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken,
-                                               const sp<IBinder>& newToken) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyFocusChangedLockedInterruptible);
-    commandEntry->oldToken = oldToken;
-    commandEntry->newToken = newToken;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) {
+    auto command = [this, token, x, y]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyDropWindow(token, x, y);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) {
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyDropWindowLockedInterruptible);
-    commandEntry->newToken = token;
-    commandEntry->x = x;
-    commandEntry->y = y;
-    postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) {
+    auto command = [this, obscuringPackage]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyUntrustedTouch(obscuringPackage);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
@@ -5566,17 +5611,11 @@
             StringPrintf("%s does not have a focused window", application->getName().c_str());
     updateLastAnrStateLocked(*application, reason);
 
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible);
-    commandEntry->inputApplicationHandle = std::move(application);
-    postCommandLocked(std::move(commandEntry));
-}
-
-void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyUntrustedTouchLockedInterruptible);
-    commandEntry->obscuringPackage = obscuringPackage;
-    postCommandLocked(std::move(commandEntry));
+    auto command = [this, application = std::move(application)]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyNoFocusedWindowAnr(application);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::updateLastAnrStateLocked(const sp<WindowInfoHandle>& window,
@@ -5607,109 +5646,24 @@
     dumpDispatchStateLocked(mLastAnrState);
 }
 
-void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) {
-    sp<Connection> connection = commandEntry->connection;
-
-    if (connection->status != Connection::STATUS_ZOMBIE) {
-        mLock.unlock();
-
-        mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
-
-        mLock.lock();
-    }
-}
-
-void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) {
-    sp<IBinder> oldToken = commandEntry->oldToken;
-    sp<IBinder> newToken = commandEntry->newToken;
-    mLock.unlock();
-    mPolicy->notifyFocusChanged(oldToken, newToken);
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) {
-    sp<IBinder> newToken = commandEntry->newToken;
-    mLock.unlock();
-    mPolicy->notifyDropWindow(newToken, commandEntry->x, commandEntry->y);
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyNoFocusedWindowAnr(commandEntry->inputApplicationHandle);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyMonitorUnresponsive(commandEntry->pid, commandEntry->reason);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyWindowResponsive(commandEntry->connectionToken);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyMonitorResponsive(commandEntry->pid);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->notifyUntrustedTouch(commandEntry->obscuringPackage);
-
-    mLock.lock();
-}
-
-void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
-        CommandEntry* commandEntry) {
-    KeyEntry& entry = *(commandEntry->keyEntry);
-    KeyEvent event = createKeyEvent(entry);
-
-    mLock.unlock();
-
-    android::base::Timer t;
-    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",
-              std::to_string(t.duration().count()).c_str());
-    }
-
-    mLock.lock();
+void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
+                                                             KeyEntry& entry) {
+    const KeyEvent event = createKeyEvent(entry);
+    nsecs_t delay = 0;
+    { // release lock
+        scoped_unlock unlock(mLock);
+        android::base::Timer t;
+        delay = mPolicy->interceptKeyBeforeDispatching(focusedWindowToken, &event,
+                                                       entry.policyFlags);
+        if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
+            ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
+                  std::to_string(t.duration().count()).c_str());
+        }
+    } // acquire lock
 
     if (delay < 0) {
         entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
-    } else if (!delay) {
+    } else if (delay == 0) {
         entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
     } else {
         entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
@@ -5717,122 +5671,37 @@
     }
 }
 
-void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-    mPolicy->onPointerDownOutsideFocus(commandEntry->newToken);
-    mLock.lock();
-}
-
-/**
- * Connection is responsive if it has no events in the waitQueue that are older than the
- * current time.
- */
-static bool isConnectionResponsive(const Connection& connection) {
-    const nsecs_t currentTime = now();
-    for (const DispatchEntry* entry : connection.waitQueue) {
-        if (entry->timeoutTime < currentTime) {
-            return false;
-        }
-    }
-    return true;
-}
-
-void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
-    sp<Connection> connection = commandEntry->connection;
-    const nsecs_t finishTime = commandEntry->eventTime;
-    uint32_t seq = commandEntry->seq;
-    const bool handled = commandEntry->handled;
-
-    // Handle post-event policy actions.
-    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
-    if (dispatchEntryIt == connection->waitQueue.end()) {
-        return;
-    }
-    DispatchEntry* dispatchEntry = *dispatchEntryIt;
-    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
-    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
-        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
-              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
-    }
-    if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
-        mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
-                                           connection->inputChannel->getConnectionToken(),
-                                           dispatchEntry->deliveryTime, commandEntry->consumeTime,
-                                           finishTime);
-    }
-
-    bool restartEvent;
-    if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
-        KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
-        restartEvent =
-                afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
-    } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
-        MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
-        restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
-                                                           handled);
-    } else {
-        restartEvent = false;
-    }
-
-    // Dequeue the event and start the next cycle.
-    // Because the lock might have been released, it is possible that the
-    // contents of the wait queue to have been drained, so we need to double-check
-    // a few things.
-    dispatchEntryIt = connection->findWaitQueueEntry(seq);
-    if (dispatchEntryIt != connection->waitQueue.end()) {
-        dispatchEntry = *dispatchEntryIt;
-        connection->waitQueue.erase(dispatchEntryIt);
-        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
-        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
-        if (!connection->responsive) {
-            connection->responsive = isConnectionResponsive(*connection);
-            if (connection->responsive) {
-                // The connection was unresponsive, and now it's responsive.
-                processConnectionResponsiveLocked(*connection);
-            }
-        }
-        traceWaitQueueLength(*connection);
-        if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
-            connection->outboundQueue.push_front(dispatchEntry);
-            traceOutboundQueueLength(*connection);
-        } else {
-            releaseDispatchEntry(dispatchEntry);
-        }
-    }
-
-    // Start the next dispatch cycle for this connection.
-    startDispatchCycleLocked(now(), connection);
-}
-
 void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) {
-    std::unique_ptr<CommandEntry> monitorUnresponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible);
-    monitorUnresponsiveCommand->pid = pid;
-    monitorUnresponsiveCommand->reason = std::move(reason);
-    postCommandLocked(std::move(monitorUnresponsiveCommand));
+    auto command = [this, pid, reason = std::move(reason)]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyMonitorUnresponsive(pid, reason);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken,
+void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
                                                           std::string reason) {
-    std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible);
-    windowUnresponsiveCommand->connectionToken = std::move(connectionToken);
-    windowUnresponsiveCommand->reason = std::move(reason);
-    postCommandLocked(std::move(windowUnresponsiveCommand));
+    auto command = [this, token, reason = std::move(reason)]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyWindowUnresponsive(token, reason);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) {
-    std::unique_ptr<CommandEntry> monitorResponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible);
-    monitorResponsiveCommand->pid = pid;
-    postCommandLocked(std::move(monitorResponsiveCommand));
+    auto command = [this, pid]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyMonitorResponsive(pid);
+    };
+    postCommandLocked(std::move(command));
 }
 
-void InputDispatcher::sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) {
-    std::unique_ptr<CommandEntry> windowResponsiveCommand = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyWindowResponsiveLockedInterruptible);
-    windowResponsiveCommand->connectionToken = std::move(connectionToken);
-    postCommandLocked(std::move(windowResponsiveCommand));
+void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) {
+    auto command = [this, connectionToken]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->notifyWindowResponsive(connectionToken);
+    };
+    postCommandLocked(std::move(command));
 }
 
 /**
@@ -5880,7 +5749,7 @@
     sendWindowResponsiveCommandLocked(connectionToken);
 }
 
-bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection,
                                                        DispatchEntry* dispatchEntry,
                                                        KeyEntry& keyEntry, bool handled) {
     if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
@@ -5905,11 +5774,12 @@
         // then cancel the associated fallback key, if any.
         if (fallbackKeyCode != -1) {
             // Dispatch the unhandled key to the policy with the cancel flag.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
-                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                  keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
+                      "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                      keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount,
+                      keyEntry.policyFlags);
+            }
             KeyEvent event = createKeyEvent(keyEntry);
             event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
 
@@ -5937,21 +5807,21 @@
         // Then ask the policy what to do with it.
         bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0;
         if (fallbackKeyCode == -1 && !initialDown) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: Skipping unhandled key event processing "
-                  "since this is not an initial down.  "
-                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                  originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: Skipping unhandled key event processing "
+                      "since this is not an initial down.  "
+                      "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                      originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
+            }
             return false;
         }
 
         // Dispatch the unhandled key to the policy.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("Unhandled key event: Asking policy to perform fallback action.  "
-              "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-              keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
-#endif
+        if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+            ALOGD("Unhandled key event: Asking policy to perform fallback action.  "
+                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                  keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
+        }
         KeyEvent event = createKeyEvent(keyEntry);
 
         mLock.unlock();
@@ -5985,19 +5855,19 @@
         // longer dispatch a fallback key to the application.
         if (fallbackKeyCode != AKEYCODE_UNKNOWN &&
             (!fallback || fallbackKeyCode != event.getKeyCode())) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            if (fallback) {
-                ALOGD("Unhandled key event: Policy requested to send key %d"
-                      "as a fallback for %d, but on the DOWN it had requested "
-                      "to send %d instead.  Fallback canceled.",
-                      event.getKeyCode(), originalKeyCode, fallbackKeyCode);
-            } else {
-                ALOGD("Unhandled key event: Policy did not request fallback for %d, "
-                      "but on the DOWN it had requested to send %d.  "
-                      "Fallback canceled.",
-                      originalKeyCode, fallbackKeyCode);
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                if (fallback) {
+                    ALOGD("Unhandled key event: Policy requested to send key %d"
+                          "as a fallback for %d, but on the DOWN it had requested "
+                          "to send %d instead.  Fallback canceled.",
+                          event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+                } else {
+                    ALOGD("Unhandled key event: Policy did not request fallback for %d, "
+                          "but on the DOWN it had requested to send %d.  "
+                          "Fallback canceled.",
+                          originalKeyCode, fallbackKeyCode);
+                }
             }
-#endif
 
             CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
                                        "canceling fallback, policy no longer desires it");
@@ -6011,18 +5881,18 @@
             }
         }
 
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-        {
-            std::string msg;
-            const KeyedVector<int32_t, int32_t>& fallbackKeys =
-                    connection->inputState.getFallbackKeys();
-            for (size_t i = 0; i < fallbackKeys.size(); i++) {
-                msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i));
+        if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+            {
+                std::string msg;
+                const KeyedVector<int32_t, int32_t>& fallbackKeys =
+                        connection->inputState.getFallbackKeys();
+                for (size_t i = 0; i < fallbackKeys.size(); i++) {
+                    msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i));
+                }
+                ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
+                      fallbackKeys.size(), msg.c_str());
             }
-            ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
-                  fallbackKeys.size(), msg.c_str());
         }
-#endif
 
         if (fallback) {
             // Restart the dispatch cycle using the fallback key.
@@ -6038,16 +5908,16 @@
             keyEntry.downTime = event.getDownTime();
             keyEntry.syntheticRepeat = false;
 
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: Dispatching fallback key.  "
-                  "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
-                  originalKeyCode, fallbackKeyCode, keyEntry.metaState);
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: Dispatching fallback key.  "
+                      "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+                      originalKeyCode, fallbackKeyCode, keyEntry.metaState);
+            }
             return true; // restart the event
         } else {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: No fallback key.");
-#endif
+            if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                ALOGD("Unhandled key event: No fallback key.");
+            }
 
             // Report the key as unhandled, since there is no fallback key.
             mReporter->reportUnhandledKey(keyEntry.id);
@@ -6056,21 +5926,12 @@
     return false;
 }
 
-bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+bool InputDispatcher::afterMotionEventLockedInterruptable(const sp<Connection>& connection,
                                                           DispatchEntry* dispatchEntry,
                                                           MotionEntry& motionEntry, bool handled) {
     return false;
 }
 
-void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType,
-                              commandEntry->displayId);
-
-    mLock.lock();
-}
-
 void InputDispatcher::traceInboundQueueLengthLocked() {
     if (ATRACE_ENABLED()) {
         ATRACE_INT("iq", mInboundQueue.size());
@@ -6182,7 +6043,7 @@
     disablePointerCaptureForcedLocked();
 
     if (mFocusedDisplayId == changes.displayId) {
-        notifyFocusChangedLocked(changes.oldFocus, changes.newFocus);
+        sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
     }
 }
 
@@ -6216,19 +6077,11 @@
 }
 
 void InputDispatcher::setPointerCaptureLocked(bool enabled) {
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doSetPointerCaptureLockedInterruptible);
-    commandEntry->enabled = enabled;
-    postCommandLocked(std::move(commandEntry));
-}
-
-void InputDispatcher::doSetPointerCaptureLockedInterruptible(
-        android::inputdispatcher::CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    mPolicy->setPointerCapture(commandEntry->enabled);
-
-    mLock.lock();
+    auto command = [this, enabled]() REQUIRES(mLock) {
+        scoped_unlock unlock(mLock);
+        mPolicy->setPointerCapture(enabled);
+    };
+    postCommandLocked(std::move(command));
 }
 
 void InputDispatcher::displayRemoved(int32_t displayId) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 2436e73..6df333a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -171,7 +171,26 @@
     std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock);
     std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock);
     std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock);
-    std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
+
+    // A command entry captures state and behavior for an action to be performed in the
+    // dispatch loop after the initial processing has taken place.  It is essentially
+    // a kind of continuation used to postpone sensitive policy interactions to a point
+    // in the dispatch loop where it is safe to release the lock (generally after finishing
+    // the critical parts of the dispatch cycle).
+    //
+    // The special thing about commands is that they can voluntarily release and reacquire
+    // the dispatcher lock at will.  Initially when the command starts running, the
+    // dispatcher lock is held.  However, if the command needs to call into the policy to
+    // do some work, it can release the lock, do the work, then reacquire the lock again
+    // before returning.
+    //
+    // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
+    // never calls into the policy while holding its lock.
+    //
+    // Commands are called with the lock held, but they can release and re-acquire the lock from
+    // within.
+    using Command = std::function<void()>;
+    std::deque<Command> mCommandQueue GUARDED_BY(mLock);
 
     DropReason mLastDropReason GUARDED_BY(mLock);
 
@@ -215,7 +234,6 @@
     sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                  int32_t y, TouchState* touchState,
                                                                  bool addOutsideTargets = false,
-                                                                 bool addPortalWindows = false,
                                                                  bool ignoreDragWindow = false)
             REQUIRES(mLock);
 
@@ -296,8 +314,8 @@
 
     // Deferred command processing.
     bool haveCommandsLocked() const REQUIRES(mLock);
-    bool runCommandsLockedInterruptible() REQUIRES(mLock);
-    void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+    bool runCommandsLockedInterruptable() REQUIRES(mLock);
+    void postCommandLocked(Command&& command) REQUIRES(mLock);
 
     nsecs_t processAnrsLocked() REQUIRES(mLock);
     std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
@@ -405,7 +423,7 @@
             DropReason& dropReason) REQUIRES(mLock);
     void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
                              const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
-    void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+    void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<SensorEntry>& entry,
                               DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
     void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
     void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
@@ -452,24 +470,11 @@
      */
     void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
 
-    /**
-     * Post `doNotifyMonitorUnresponsiveLockedInterruptible` command.
-     */
     void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock);
-    /**
-     * Post `doNotifyWindowUnresponsiveLockedInterruptible` command.
-     */
-    void sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, std::string reason)
+    void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken, std::string reason)
             REQUIRES(mLock);
-    /**
-     * Post `doNotifyMonitorResponsiveLockedInterruptible` command.
-     */
     void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock);
-    /**
-     * Post `doNotifyWindowResponsiveLockedInterruptible` command.
-     */
-    void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
-
+    void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
 
     // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
     // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -497,9 +502,7 @@
     android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
             nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
             nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
-    std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
-            int32_t displayId,
-            const std::vector<sp<android::gui::WindowInfoHandle>>& portalWindows) const
+    std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId) const
             REQUIRES(mLock);
     std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
             const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
@@ -605,53 +608,30 @@
             REQUIRES(mLock);
 
     // Interesting events that we might like to log or tell the framework about.
-    void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                       uint32_t seq, bool handled, nsecs_t consumeTime)
+    void doDispatchCycleFinishedCommand(nsecs_t finishTime, const sp<Connection>& connection,
+                                        uint32_t seq, bool handled, nsecs_t consumeTime)
             REQUIRES(mLock);
-    void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
-            REQUIRES(mLock);
+    void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
+                                                KeyEntry& entry) REQUIRES(mLock);
     void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
-    void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
+    void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
             REQUIRES(mLock);
-    void notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
+    void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
+    void sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) REQUIRES(mLock);
     void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
-    void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
     void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window,
                                   const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const InputApplicationHandle& application,
                                   const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
             REQUIRES(mLock);
-
-    // Outbound policy interactions.
-    void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
-    void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-
-    // ANR-related callbacks - start
-    void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    // ANR-related callbacks - end
-    void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
-    void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doSetPointerCaptureLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+    bool afterKeyEventLockedInterruptable(const sp<Connection>& connection,
                                           DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
                                           bool handled) REQUIRES(mLock);
-    bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+    bool afterMotionEventLockedInterruptable(const sp<Connection>& connection,
                                              DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
                                              bool handled) REQUIRES(mLock);
-    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
 
     // Statistics gathering.
     LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index d634dcd..52f189c 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -50,13 +50,12 @@
  * key-value pair. Equivalent to the imaginary std api std::multimap::erase(key, value).
  */
 template <typename K, typename V>
-static void eraseByKeyAndValue(std::multimap<K, V>& map, K key, V value) {
-    auto iterpair = map.equal_range(key);
-
-    for (auto it = iterpair.first; it != iterpair.second; ++it) {
+static void eraseByValue(std::multimap<K, V>& map, const V& value) {
+    for (auto it = map.begin(); it != map.end();) {
         if (it->second == value) {
-            map.erase(it);
-            break;
+            it = map.erase(it);
+        } else {
+            it++;
         }
     }
 }
@@ -76,9 +75,7 @@
         // confuse us by reporting the rest of the timeline for one of them. This should happen
         // rarely, so we won't lose much data
         mTimelines.erase(it);
-        // In case we have another input event with a different id and at the same eventTime,
-        // only erase this specific inputEventId.
-        eraseByKeyAndValue(mEventTimes, eventTime, inputEventId);
+        eraseByValue(mEventTimes, inputEventId);
         return;
     }
     mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime));
@@ -90,7 +87,8 @@
                                         nsecs_t finishTime) {
     const auto it = mTimelines.find(inputEventId);
     if (it == mTimelines.end()) {
-        // It's possible that an app sends a bad (or late)'Finish' signal, since it's free to do
+        // This could happen if we erased this event when duplicate events were detected. It's
+        // also possible that an app sent a bad (or late) 'Finish' signal, since it's free to do
         // anything in its process. Just drop the report and move on.
         return;
     }
@@ -120,7 +118,8 @@
         std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
     const auto it = mTimelines.find(inputEventId);
     if (it == mTimelines.end()) {
-        // It's possible that an app sends a bad (or late) 'Timeline' signal, since it's free to do
+        // This could happen if we erased this event when duplicate events were detected. It's
+        // also possible that an app sent a bad (or late) 'Timeline' signal, since it's free to do
         // anything in its process. Just drop the report and move on.
         return;
     }
@@ -166,14 +165,6 @@
     }
 }
 
-void LatencyTracker::reportNow() {
-    for (const auto& [inputEventId, timeline] : mTimelines) {
-        mTimelineProcessor->processTimeline(timeline);
-    }
-    mTimelines.clear();
-    mEventTimes.clear();
-}
-
 std::string LatencyTracker::dump(const char* prefix) {
     return StringPrintf("%sLatencyTracker:\n", prefix) +
             StringPrintf("%s  mTimelines.size() = %zu\n", prefix, mTimelines.size()) +
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 289b8ed..4b0c618 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -43,6 +43,12 @@
     LatencyTracker(InputEventTimelineProcessor* processor);
     /**
      * Start keeping track of an event identified by inputEventId. This must be called first.
+     * If duplicate events are encountered (events that have the same eventId), none of them will be
+     * tracked. This is because there is not enough information to correctly track them. The api's
+     * 'trackFinishedEvent' and 'trackGraphicsLatency' only contain the inputEventId, and not the
+     * eventTime. Even if eventTime was provided, there would still be a possibility of having
+     * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we
+     * must drop all duplicate data.
      */
     void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime);
     void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
@@ -50,14 +56,6 @@
     void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
                               std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
 
-    /**
-     * Report all collected events immediately, even if some of them are currently incomplete
-     * and may receive 'trackFinishedEvent' or 'trackGraphicsLatency' calls in the future.
-     * This is useful for tests. Otherwise, tests would have to inject additional "future" events,
-     * which is not convenient.
-     */
-    void reportNow();
-
     std::string dump(const char* prefix);
 
 private:
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 20b6ead..b7ed658 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -37,7 +37,6 @@
     source = 0;
     displayId = ADISPLAY_ID_NONE;
     windows.clear();
-    portalWindows.clear();
     gestureMonitors.clear();
 }
 
@@ -48,7 +47,6 @@
     source = other.source;
     displayId = other.displayId;
     windows = other.windows;
-    portalWindows = other.portalWindows;
     gestureMonitors = other.gestureMonitors;
 }
 
@@ -77,16 +75,6 @@
     windows.push_back(touchedWindow);
 }
 
-void TouchState::addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle) {
-    size_t numWindows = portalWindows.size();
-    for (size_t i = 0; i < numWindows; i++) {
-        if (portalWindows[i] == windowHandle) {
-            return;
-        }
-    }
-    portalWindows.push_back(windowHandle);
-}
-
 void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) {
     const size_t newSize = gestureMonitors.size() + newMonitors.size();
     gestureMonitors.reserve(newSize);
@@ -119,7 +107,6 @@
 
 void TouchState::filterNonMonitors() {
     windows.clear();
-    portalWindows.clear();
 }
 
 sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index a4e52b0..579b868 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -36,11 +36,6 @@
     int32_t displayId; // id to the display that currently has a touch, others are rejected
     std::vector<TouchedWindow> windows;
 
-    // This collects the portal windows that the touch has gone through. Each portal window
-    // targets a display (embedded display for most cases). With this info, we can add the
-    // monitoring channels of the displays touched.
-    std::vector<sp<android::gui::WindowInfoHandle>> portalWindows;
-
     std::vector<TouchedMonitor> gestureMonitors;
 
     TouchState();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 50e1854..0fc4708 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,6 +16,7 @@
 
 #include "../dispatcher/InputDispatcher.h"
 
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
 #include <binder/Binder.h>
@@ -678,7 +679,11 @@
 
 // --- InputDispatcherTest SetInputWindowTest ---
 static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
-static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds(
+        android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+        android::base::HwTimeoutMultiplier());
 
 class FakeApplicationHandle : public InputApplicationHandle {
 public:
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index e7e1937..89c0741 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -16,6 +16,7 @@
 
 #include "../dispatcher/LatencyTracker.h"
 
+#include <android-base/properties.h>
 #include <binder/Binder.h>
 #include <gtest/gtest.h>
 #include <inttypes.h>
@@ -23,11 +24,16 @@
 
 #define TAG "LatencyTracker_test"
 
+using android::base::HwTimeoutMultiplier;
 using android::inputdispatcher::InputEventTimeline;
 using android::inputdispatcher::LatencyTracker;
 
 namespace android::inputdispatcher {
 
+const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds(
+        android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+        HwTimeoutMultiplier());
+
 InputEventTimeline getTestTimeline() {
     InputEventTimeline t(
             /*isDown*/ true,
@@ -57,6 +63,8 @@
     }
     void TearDown() override {}
 
+    void triggerEventReporting(nsecs_t lastEventTime);
+
     void assertReceivedTimeline(const InputEventTimeline& timeline);
     /**
      * Timelines can be received in any order (order is not guaranteed). So if we are expecting more
@@ -72,8 +80,17 @@
     std::deque<InputEventTimeline> mReceivedTimelines;
 };
 
+/**
+ * Send an event that would trigger the reporting of all of the events that are at least as old as
+ * the provided 'lastEventTime'.
+ */
+void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) {
+    const nsecs_t triggerEventTime =
+            lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1;
+    mTracker->trackListener(1 /*inputEventId*/, true /*isDown*/, triggerEventTime, 3 /*readTime*/);
+}
+
 void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
-    mTracker->reportNow();
     ASSERT_FALSE(mReceivedTimelines.empty());
     const InputEventTimeline& t = mReceivedTimelines.front();
     ASSERT_EQ(timeline, t);
@@ -88,7 +105,6 @@
  * equal element in B, and for every element in B there is an equal element in A.
  */
 void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines) {
-    mTracker->reportNow();
     ASSERT_EQ(timelines.size(), mReceivedTimelines.size());
     for (const InputEventTimeline& expectedTimeline : timelines) {
         bool found = false;
@@ -121,6 +137,7 @@
  */
 TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
     mTracker->trackListener(1 /*inputEventId*/, false /*isDown*/, 2 /*eventTime*/, 3 /*readTime*/);
+    triggerEventReporting(2 /*eventTime*/);
     assertReceivedTimeline(InputEventTimeline{false, 2, 3});
 }
 
@@ -130,6 +147,7 @@
 TEST_F(LatencyTrackerTest, TrackFinishedEvent_DoesNotTriggerReporting) {
     mTracker->trackFinishedEvent(1 /*inputEventId*/, connection1, 2 /*deliveryTime*/,
                                  3 /*consumeTime*/, 4 /*finishTime*/);
+    triggerEventReporting(4 /*eventTime*/);
     assertReceivedTimelines({});
 }
 
@@ -141,6 +159,7 @@
     graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
     graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
     mTracker->trackGraphicsLatency(1 /*inputEventId*/, connection2, graphicsTimeline);
+    triggerEventReporting(3 /*eventTime*/);
     assertReceivedTimelines({});
 }
 
@@ -155,9 +174,30 @@
                                  expectedCT.consumeTime, expectedCT.finishTime);
     mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
 
+    triggerEventReporting(expected.eventTime);
     assertReceivedTimeline(expected);
 }
 
+/**
+ * Send 2 events with the same inputEventId, but different eventTime's. Ensure that no crash occurs,
+ * and that the tracker drops such events completely.
+ */
+TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) {
+    constexpr nsecs_t inputEventId = 1;
+    constexpr nsecs_t readTime = 3; // does not matter for this test
+    constexpr bool isDown = true;   // does not matter for this test
+
+    // In the following 2 calls to trackListener, the inputEventId's are the same, but event times
+    // are different.
+    mTracker->trackListener(inputEventId, isDown, 1 /*eventTime*/, readTime);
+    mTracker->trackListener(inputEventId, isDown, 2 /*eventTime*/, readTime);
+
+    triggerEventReporting(2 /*eventTime*/);
+    // Since we sent duplicate input events, the tracker should just delete all of them, because it
+    // does not have enough information to properly track them.
+    assertReceivedTimelines({});
+}
+
 TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) {
     constexpr int32_t inputEventId1 = 1;
     InputEventTimeline timeline1(
@@ -204,6 +244,7 @@
     mTracker->trackGraphicsLatency(inputEventId2, connection2,
                                    connectionTimeline2.graphicsTimeline);
     // Now both events should be completed
+    triggerEventReporting(timeline2.eventTime);
     assertReceivedTimelines({timeline1, timeline2});
 }
 
@@ -228,6 +269,7 @@
     mTracker->trackGraphicsLatency(1 /*inputEventId*/, token, expectedCT.graphicsTimeline);
 
     expectedTimelines[0].connectionTimelines.emplace(token, std::move(expectedCT));
+    triggerEventReporting(timeline.eventTime);
     assertReceivedTimelines(expectedTimelines);
 }
 
@@ -246,6 +288,7 @@
     mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
 
     mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+    triggerEventReporting(expected.eventTime);
     assertReceivedTimeline(
             InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime});
 }
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..df4db19
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+
+cc_fuzz {
+    name: "inputflinger_latencytracker_fuzzer",
+    defaults: [
+        "inputflinger_defaults",
+    ],
+    include_dirs: [
+        "frameworks/native/services/inputflinger",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libui",
+        "libutils",
+        "libinput",
+        "libinputflinger",
+    ],
+    srcs: [
+        "LatencyTrackerFuzzer.cpp",
+    ],
+}
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
new file mode 100644
index 0000000..4f066ad
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include "dispatcher/LatencyTracker.h"
+
+namespace android {
+
+namespace inputdispatcher {
+
+/**
+ * A processor of InputEventTimelines that does nothing with the provided data.
+ */
+class EmptyProcessor : public InputEventTimelineProcessor {
+public:
+    /**
+     * Just ignore the provided timeline
+     */
+    void processTimeline(const InputEventTimeline& timeline) override {
+        for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) {
+            connectionTimeline.isComplete();
+        }
+    };
+};
+
+static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp,
+                                      std::array<sp<IBinder>, 10>& tokens) {
+    const bool useExistingToken = fdp.ConsumeBool();
+    if (useExistingToken) {
+        return tokens[fdp.ConsumeIntegralInRange<size_t>(0ul, tokens.size() - 1)];
+    }
+    return new BBinder();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+
+    EmptyProcessor emptyProcessor;
+    LatencyTracker tracker(&emptyProcessor);
+
+    // Make some pre-defined tokens to ensure that some timelines are complete.
+    std::array<sp<IBinder> /*token*/, 10> predefinedTokens;
+    for (size_t i = 0; i < predefinedTokens.size(); i++) {
+        predefinedTokens[i] = new BBinder();
+    }
+
+    // Randomly invoke LatencyTracker api's until randomness is exhausted.
+    while (fdp.remaining_bytes() > 0) {
+        fdp.PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+                    int32_t isDown = fdp.ConsumeBool();
+                    nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
+                    nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
+                    tracker.trackListener(inputEventId, isDown, eventTime, readTime);
+                },
+                [&]() -> void {
+                    int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+                    sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
+                    nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>();
+                    nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>();
+                    nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>();
+                    tracker.trackFinishedEvent(inputEventId, connectionToken, deliveryTime,
+                                               consumeTime, finishTime);
+                },
+                [&]() -> void {
+                    int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+                    sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
+                    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+                    for (size_t i = 0; i < graphicsTimeline.size(); i++) {
+                        graphicsTimeline[i] = fdp.ConsumeIntegral<nsecs_t>();
+                    }
+                    tracker.trackGraphicsLatency(inputEventId, connectionToken, graphicsTimeline);
+                },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
\ No newline at end of file
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index c233bf0..c351676 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -168,7 +168,7 @@
                         }
                     }
 
-                    // Sanity check and clamp power if it is 0 (or close)
+                    // Check and clamp power if it is 0 (or close)
                     constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
                     if (sensor.power < MIN_POWER_MA) {
                         ALOGI("%s's reported power %f invalid, clamped to %f",
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 9ce8d9b..c58e992 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -867,7 +867,7 @@
             } else if (numBytesRead == sizeof(uint32_t)) {
                 uint32_t numAcks = 0;
                 memcpy(&numAcks, buf, numBytesRead);
-                // Sanity check to ensure  there are no read errors in recv, numAcks is always
+                // Check to ensure  there are no read errors in recv, numAcks is always
                 // within the range and not zero. If any of the above don't hold reset
                 // mWakeLockRefCount to zero.
                 if (numAcks > 0 && numAcks < mWakeLockRefCount) {
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 726fe8e..3081b1a 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1197,7 +1197,7 @@
     // We have a dynamic sensor.
 
     if (!sHmacGlobalKeyIsValid) {
-        // Rather than risk exposing UUIDs, we cripple dynamic sensors.
+        // Rather than risk exposing UUIDs, we slow down dynamic sensors.
         ALOGW("HMAC key failure; dynamic sensor getId() will be wrong.");
         return 0;
     }
@@ -1223,7 +1223,7 @@
              sHmacGlobalKey, sizeof(sHmacGlobalKey),
              uuidAndApp, sizeof(uuidAndApp),
              hash, &hashLen) == nullptr) {
-        // Rather than risk exposing UUIDs, we cripple dynamic sensors.
+        // Rather than risk exposing UUIDs, we slow down dynamic sensors.
         ALOGW("HMAC failure; dynamic sensor getId() will be wrong.");
         return 0;
     }
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 71db330..b600fad 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -152,39 +152,10 @@
         return result;
     }
 
-    if (CC_UNLIKELY(mBufferInfo.mBuffer == 0)) {
-        // the texture has not been created yet, this Layer has
-        // in fact never been drawn into. This happens frequently with
-        // SurfaceView because the WindowManager can't know when the client
-        // has drawn the first time.
-
-        // If there is nothing under us, we paint the screen in black, otherwise
-        // we just skip this update.
-
-        // figure out if there is something below us
-        Region under;
-        bool finished = false;
-        mFlinger->mDrawingState.traverseInZOrder([&](Layer* layer) {
-            if (finished || layer == static_cast<BufferLayer const*>(this)) {
-                finished = true;
-                return;
-            }
-
-            under.orSelf(layer->getScreenBounds());
-        });
-        // if not everything below us is covered, we plug the holes!
-        Region holes(targetSettings.clip.subtract(under));
-        if (!holes.isEmpty()) {
-            targetSettings.clearRegion.orSelf(holes);
-        }
-
-        if (mSidebandStream != nullptr) {
-            // For surfaceview of tv sideband, there is no activeBuffer
-            // in bufferqueue, we need return LayerSettings.
-            return result;
-        } else {
-            return std::nullopt;
-        }
+    if (CC_UNLIKELY(mBufferInfo.mBuffer == 0) && mSidebandStream != nullptr) {
+        // For surfaceview of tv sideband, there is no activeBuffer
+        // in bufferqueue, we need return LayerSettings.
+        return result;
     }
     const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
             ((isSecure() || isProtected()) && !targetSettings.isSecure);
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 99e470d..81254ac 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -153,11 +153,12 @@
 }
 
 bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
+    Mutex::Autolock lock(mQueueItemLock);
+
     if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
         return true;
     }
 
-    Mutex::Autolock lock(mQueueItemLock);
     return mQueueItems[0].item.mTimestamp <= expectedPresentTime;
 }
 
@@ -269,13 +270,15 @@
         // and return early
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
-            mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
-            if (mQueueItems[0].surfaceFrame) {
-                addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
+            if (mQueuedFrames > 0) {
+                mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+                mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+                if (mQueueItems[0].surfaceFrame) {
+                    addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
+                }
+                mQueueItems.erase(mQueueItems.begin());
+                mQueuedFrames--;
             }
-            mQueueItems.erase(mQueueItems.begin());
-            mQueuedFrames--;
         }
         return BAD_VALUE;
     } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
@@ -305,6 +308,7 @@
         return BAD_VALUE;
     }
 
+    bool more_frames_pending = false;
     if (queuedBuffer) {
         // Autolock scope
         auto currentFrameNumber = mConsumer->getFrameNumber();
@@ -313,7 +317,7 @@
 
         // Remove any stale buffers that have been dropped during
         // updateTexImage
-        while (mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
+        while (mQueuedFrames > 0 && mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
             mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
             mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
             if (mQueueItems[0].surfaceFrame) {
@@ -334,11 +338,12 @@
                                               latchTime);
         }
         mQueueItems.erase(mQueueItems.begin());
+        more_frames_pending = (mQueuedFrames.fetch_sub(1) > 1);
     }
 
     // Decrement the queued-frames count.  Signal another event if we
     // have more frames pending.
-    if ((queuedBuffer && mQueuedFrames.fetch_sub(1) > 1) || mAutoRefresh) {
+    if ((queuedBuffer && more_frames_pending) || mAutoRefresh) {
         mFlinger->signalLayerUpdate();
     }
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index cd531d6..4d86598 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -14,11 +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"
-#pragma clang diagnostic ignored "-Wextra"
-
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "BufferStateLayer"
@@ -596,7 +591,7 @@
     return true;
 }
 
-Rect BufferStateLayer::getBufferSize(const State& s) const {
+Rect BufferStateLayer::getBufferSize(const State& /*s*/) const {
     // for buffer state layers we use the display frame size as the buffer size.
 
     if (mBufferInfo.mBuffer == nullptr) {
@@ -618,7 +613,7 @@
         }
     }
 
-    return Rect(0, 0, bufWidth, bufHeight);
+    return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
 }
 
 FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
@@ -817,7 +812,7 @@
     eraseBufferLocked(clientCacheId);
 }
 
-uint32_t BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
+int BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
     std::lock_guard<std::mutex> lock(mMutex);
     auto itr = mCachedBuffers.find(clientCacheId);
     if (itr == mCachedBuffers.end()) {
@@ -828,7 +823,7 @@
     return hwcCacheSlot;
 }
 
-uint32_t BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
+int BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
         REQUIRES(mMutex) {
     if (!clientCacheId.isValid()) {
         ALOGE("invalid process, returning invalid slot");
@@ -837,17 +832,17 @@
 
     ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this));
 
-    uint32_t hwcCacheSlot = getFreeHwcCacheSlot();
+    int hwcCacheSlot = getFreeHwcCacheSlot();
     mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
     return hwcCacheSlot;
 }
 
-uint32_t BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
+int BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
     if (mFreeHwcCacheSlots.empty()) {
         evictLeastRecentlyUsed();
     }
 
-    uint32_t hwcCacheSlot = mFreeHwcCacheSlots.top();
+    int hwcCacheSlot = mFreeHwcCacheSlots.top();
     mFreeHwcCacheSlots.pop();
     return hwcCacheSlot;
 }
@@ -934,8 +929,8 @@
         return false;
     }
 
-    uint32_t bufferWidth = s.buffer->getBuffer()->width;
-    uint32_t bufferHeight = s.buffer->getBuffer()->height;
+    int32_t bufferWidth = s.buffer->getBuffer()->width;
+    int32_t bufferHeight = s.buffer->getBuffer()->height;
 
     // Undo any transformations on the buffer and return the result.
     if (s.bufferTransform & ui::Transform::ROT_90) {
@@ -994,6 +989,3 @@
 }
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 0a0527c..124e91a 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -178,19 +178,19 @@
     class HwcSlotGenerator : public ClientCache::ErasedRecipient {
     public:
         HwcSlotGenerator() {
-            for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+            for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
                 mFreeHwcCacheSlots.push(i);
             }
         }
 
         void bufferErased(const client_cache_t& clientCacheId);
 
-        uint32_t getHwcCacheSlot(const client_cache_t& clientCacheId);
+        int getHwcCacheSlot(const client_cache_t& clientCacheId);
 
     private:
         friend class SlotGenerationTest;
-        uint32_t addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
-        uint32_t getFreeHwcCacheSlot() REQUIRES(mMutex);
+        int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
+        int getFreeHwcCacheSlot() REQUIRES(mMutex);
         void evictLeastRecentlyUsed() REQUIRES(mMutex);
         void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
 
@@ -202,11 +202,10 @@
 
         std::mutex mMutex;
 
-        std::unordered_map<client_cache_t,
-                           std::pair<uint32_t /*HwcCacheSlot*/, uint32_t /*counter*/>,
+        std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
                            CachedBufferHash>
                 mCachedBuffers GUARDED_BY(mMutex);
-        std::stack<uint32_t /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
+        std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
 
         // The cache increments this counter value when a slot is updated or used.
         // Used to track the least recently-used buffer
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index aac6c91..8da2e24 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -14,10 +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"
-
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -132,6 +128,3 @@
 
 // ---------------------------------------------------------------------------
 }; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 554e2f4..95d553d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -47,9 +47,6 @@
     // All the layers that have queued updates.
     Layers layersWithQueuedFrames;
 
-    // If true, forces the entire display to be considered dirty and repainted
-    bool repaintEverything{false};
-
     // Controls how the color mode is chosen for an output
     OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 14eddb1..98c4af4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -36,18 +36,12 @@
 struct DisplayCreationArgs {
     DisplayId id;
 
-    // Unset for virtual displays
-    std::optional<ui::DisplayConnectionType> connectionType;
-
     // Size of the display in pixels
-    ui::Size pixels = ui::Size::INVALID;
+    ui::Size pixels = ui::kInvalidSize;
 
     // True if this display should be considered secure
     bool isSecure = false;
 
-    // Gives the initial layer stack id to be used for the display
-    uint32_t layerStackId = ~0u;
-
     // Optional pointer to the power advisor interface, if one is needed for
     // this display.
     Hwc2::PowerAdvisor* powerAdvisor = nullptr;
@@ -69,11 +63,6 @@
         return *this;
     }
 
-    DisplayCreationArgsBuilder& setConnectionType(ui::DisplayConnectionType connectionType) {
-        mArgs.connectionType = connectionType;
-        return *this;
-    }
-
     DisplayCreationArgsBuilder& setPixels(ui::Size pixels) {
         mArgs.pixels = pixels;
         return *this;
@@ -84,11 +73,6 @@
         return *this;
     }
 
-    DisplayCreationArgsBuilder& setLayerStackId(uint32_t layerStackId) {
-        mArgs.layerStackId = layerStackId;
-        return *this;
-    }
-
     DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
         mArgs.powerAdvisor = powerAdvisor;
         return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index e51019a..6d1017f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -115,10 +115,6 @@
         // If set to true, the target buffer has protected content support.
         const bool supportsProtectedContent;
 
-        // Modified by each call to prepareClientComposition to indicate the
-        // region of the target buffer that should be cleared.
-        Region& clearRegion;
-
         // Viewport of the target being rendered to. This is used to determine
         // the shadow light position.
         const Rect& viewport;
@@ -177,11 +173,10 @@
 
 static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
                               const LayerFE::ClientCompositionTargetSettings& rhs) {
-    return lhs.clip.hasSameRects(rhs.clip) &&
-            lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
+    return lhs.clip.hasSameRects(rhs.clip) && lhs.needsFiltering == rhs.needsFiltering &&
+            lhs.isSecure == rhs.isSecure &&
             lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
-            lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport &&
-            lhs.dataspace == rhs.dataspace &&
+            lhs.viewport == rhs.viewport && lhs.dataspace == rhs.dataspace &&
             lhs.realContentIsVisible == rhs.realContentIsVisible &&
             lhs.clearContent == rhs.clearContent;
 }
@@ -202,8 +197,6 @@
     *os << "\n    .needsFiltering = " << settings.needsFiltering;
     *os << "\n    .isSecure = " << settings.isSecure;
     *os << "\n    .supportsProtectedContent = " << settings.supportsProtectedContent;
-    *os << "\n    .clearRegion = ";
-    PrintTo(settings.clearRegion, os);
     *os << "\n    .viewport = ";
     PrintTo(settings.viewport, os);
     *os << "\n    .dataspace = ";
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index a45be8a..ecb4e6d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -22,6 +22,7 @@
 #include <math/mat4.h>
 #include <ui/BlurRegion.h>
 #include <ui/FloatRect.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -93,11 +94,9 @@
     /*
      * Visibility state
      */
-    // the layer stack this layer belongs to
-    std::optional<uint32_t> layerStackId;
 
-    // If true, this layer should be only visible on the internal display
-    bool internalOnly{false};
+    // The filter that determines which outputs include this layer
+    ui::LayerFilter outputFilter;
 
     // If false, this layer should not be considered visible
     bool isVisible{true};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 0ef0b99..a33b57d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -28,6 +28,7 @@
 #include <renderengine/LayerSettings.h>
 #include <ui/Fence.h>
 #include <ui/GraphicTypes.h>
+#include <ui/LayerStack.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
 #include <utils/StrongPointer.h>
@@ -180,9 +181,8 @@
     // output.
     virtual ui::Transform::RotationFlags getTransformHint() const = 0;
 
-    // Sets the layer stack filtering settings for this output. See
-    // belongsInOutput for full details.
-    virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
+    // Sets the filter for this output. See Output::includesLayer.
+    virtual void setLayerFilter(ui::LayerFilter) = 0;
 
     // Sets the output color mode
     virtual void setColorProfile(const ColorProfile&) = 0;
@@ -221,20 +221,12 @@
     virtual OutputCompositionState& editState() = 0;
 
     // Gets the dirty region in layer stack space.
-    // If repaintEverything is true, this will be the full display bounds.
-    virtual Region getDirtyRegion(bool repaintEverything) const = 0;
+    virtual Region getDirtyRegion() const = 0;
 
-    // Tests whether a given layerStackId belongs in this output.
-    // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
-    // unless the layer should display on the primary output only and this is not the primary output
-
-    // A layer belongs to the output if its layerStackId matches. Additionally
-    // if the layer should only show in the internal (primary) display only and
-    // this output allows that.
-    virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0;
-
-    // Determines if a layer belongs to the output.
-    virtual bool belongsInOutput(const sp<LayerFE>&) const = 0;
+    // Returns whether the output includes a layer, based on their respective filters.
+    // See Output::setLayerFilter.
+    virtual bool includesLayer(ui::LayerFilter) const = 0;
+    virtual bool includesLayer(const sp<LayerFE>&) const = 0;
 
     // Returns a pointer to the output layer corresponding to the given layer on
     // this output, or nullptr if the layer does not have one
@@ -294,7 +286,7 @@
     virtual bool getSkipColorTransform() const = 0;
     virtual FrameFences presentAndGetFrameFences() = 0;
     virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
-            bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0;
+            bool supportsProtectedContent, ui::Dataspace outputDataspace) = 0;
     virtual void appendRegionFlashRequests(
             const Region& flashRegion,
             std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index bb540ea..b407267 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -83,9 +83,8 @@
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
 
 private:
-    bool mIsVirtual = false;
-    bool mIsDisconnected = false;
     DisplayId mId;
+    bool mIsDisconnected = false;
     Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
index 6b9597b..7521324 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -22,6 +22,7 @@
 
 #include <math/mat4.h>
 #include <ui/FloatRect.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/StretchEffect.h>
@@ -52,13 +53,14 @@
     dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
 }
 
-void dumpVal(std::string& out, const char* name, const FloatRect& rect);
-void dumpVal(std::string& out, const char* name, const Rect& rect);
-void dumpVal(std::string& out, const char* name, const Region& region);
-void dumpVal(std::string& out, const char* name, const ui::Transform&);
-void dumpVal(std::string& out, const char* name, const ui::Size&);
+void dumpVal(std::string& out, const char* name, ui::LayerFilter);
+void dumpVal(std::string& out, const char* name, ui::Size);
 
-void dumpVal(std::string& out, const char* name, const mat4& tr);
+void dumpVal(std::string& out, const char* name, const FloatRect&);
+void dumpVal(std::string& out, const char* name, const Rect&);
+void dumpVal(std::string& out, const char* name, const Region&);
+void dumpVal(std::string& out, const char* name, const ui::Transform&);
+void dumpVal(std::string& out, const char* name, const mat4&);
 void dumpVal(std::string& out, const char* name, const StretchEffect&);
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index ddcc907..6d49ce6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -23,6 +23,7 @@
 #include <compositionengine/impl/planner/Planner.h>
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/LayerSettings.h>
+
 #include <memory>
 #include <utility>
 #include <vector>
@@ -45,7 +46,7 @@
     void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                        const Rect& orientedDisplaySpaceRect) override;
     void setDisplaySize(const ui::Size&) override;
-    void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
+    void setLayerFilter(ui::LayerFilter) override;
     ui::Transform::RotationFlags getTransformHint() const override;
 
     void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
@@ -64,9 +65,10 @@
     compositionengine::RenderSurface* getRenderSurface() const override;
     void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override;
 
-    Region getDirtyRegion(bool repaintEverything) const override;
-    bool belongsInOutput(std::optional<uint32_t>, bool) const override;
-    bool belongsInOutput(const sp<LayerFE>&) const override;
+    Region getDirtyRegion() const override;
+
+    bool includesLayer(ui::LayerFilter) const override;
+    bool includesLayer(const sp<LayerFE>&) const override;
 
     compositionengine::OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const override;
 
@@ -111,8 +113,7 @@
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentAndGetFrameFences() override;
     std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
-            bool supportsProtectedContent, Region& clearRegion,
-            ui::Dataspace outputDataspace) override;
+            bool supportsProtectedContent, ui::Dataspace outputDataspace) override;
     void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
     void setExpensiveRenderingExpected(bool enabled) override;
     void dumpBase(std::string&) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index f34cb94..44f754f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -32,6 +32,7 @@
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <compositionengine/ProjectionSpace.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -59,11 +60,8 @@
     // If true, the current frame reused the buffer from a previous client composition
     bool reusedClientComposition{false};
 
-    // If true, this output displays layers that are internal-only
-    bool layerStackInternal{false};
-
-    // The layer stack to display on this display
-    uint32_t layerStackId{~0u};
+    // The conditions for including a layer on this output
+    ui::LayerFilter layerFilter;
 
     // The common space for all layers in the layer stack. layerStackSpace.content is the Rect
     // which gets projected on the display. The orientation of this space is always ROTATION_0.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 8fdf3ae..344b2f9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -40,9 +40,12 @@
     MOCK_METHOD1(setLayerCachingTexturePoolEnabled, void(bool));
     MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
     MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
-    MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
     MOCK_CONST_METHOD0(getTransformHint, ui::Transform::RotationFlags());
 
+    MOCK_METHOD(void, setLayerFilter, (ui::LayerFilter));
+    MOCK_METHOD(bool, includesLayer, (ui::LayerFilter), (const));
+    MOCK_METHOD(bool, includesLayer, (const sp<compositionengine::LayerFE>&), (const));
+
     MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
     MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
     MOCK_METHOD2(setDisplayBrightness, void(float, float));
@@ -62,9 +65,7 @@
     MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
     MOCK_METHOD0(editState, OutputCompositionState&());
 
-    MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
-    MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool));
-    MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
+    MOCK_METHOD(Region, getDirtyRegion, (), (const));
 
     MOCK_CONST_METHOD1(getOutputLayerForLayer,
                        compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&));
@@ -113,8 +114,8 @@
     MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&));
     MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
 
-    MOCK_METHOD3(generateClientCompositionRequests,
-                 std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+    MOCK_METHOD2(generateClientCompositionRequests,
+                 std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace));
     MOCK_METHOD2(appendRegionFlashRequests,
                  void(const Region&, std::vector<LayerFE::LayerSettings>&));
     MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 2f2c686..6b9ea87 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -51,12 +51,9 @@
 
 void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
     mId = args.id;
-    mIsVirtual = !args.connectionType;
     mPowerAdvisor = args.powerAdvisor;
     editState().isSecure = args.isSecure;
     editState().displaySpace.bounds = Rect(args.pixels);
-    setLayerStackFilter(args.layerStackId,
-                        args.connectionType == ui::DisplayConnectionType::Internal);
     setName(args.name);
 }
 
@@ -73,7 +70,7 @@
 }
 
 bool Display::isVirtual() const {
-    return mIsVirtual;
+    return VirtualDisplayId::tryCast(mId).has_value();
 }
 
 std::optional<DisplayId> Display::getDisplayId() const {
@@ -117,8 +114,8 @@
         return;
     }
 
-    if (mIsVirtual) {
-        ALOGW("%s: Invalid operation on virtual display", __FUNCTION__);
+    if (isVirtual()) {
+        ALOGW("%s: Invalid operation on virtual display", __func__);
         return;
     }
 
@@ -136,7 +133,7 @@
     StringAppendF(&out, "   Composition Display State: [\"%s\"]", getName().c_str());
 
     out.append("\n   ");
-    dumpVal(out, "isVirtual", mIsVirtual);
+    dumpVal(out, "isVirtual", isVirtual());
     dumpVal(out, "DisplayId", to_string(mId));
     out.append("\n");
 
@@ -364,8 +361,7 @@
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    if (GpuVirtualDisplayId::tryCast(mId) &&
-        getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
+    if (GpuVirtualDisplayId::tryCast(mId) && getDirtyRegion().isEmpty()) {
         ALOGV("Skipping display composition");
         return;
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 5565396..01c368d 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -63,6 +63,18 @@
     dumpVal(out, name, valueName.c_str(), value);
 }
 
+void dumpVal(std::string& out, const char* name, ui::LayerFilter filter) {
+    out.append(name);
+    out.append("={");
+    dumpVal(out, "layerStack", filter.layerStack.id);
+    dumpVal(out, "toInternalDisplay", filter.toInternalDisplay);
+    out.push_back('}');
+}
+
+void dumpVal(std::string& out, const char* name, ui::Size size) {
+    StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
+}
+
 void dumpVal(std::string& out, const char* name, const FloatRect& rect) {
     StringAppendF(&out, "%s=[%f %f %f %f] ", name, rect.left, rect.top, rect.right, rect.bottom);
 }
@@ -80,10 +92,6 @@
     out.append(" ");
 }
 
-void dumpVal(std::string& out, const char* name, const ui::Size& size) {
-    StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
-}
-
 void dumpVal(std::string& out, const char* name, const mat4& tr) {
     StringAppendF(&out,
                   "%s=["
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3310a71..0ff5eca 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -227,11 +227,8 @@
     return static_cast<ui::Transform::RotationFlags>(getState().transform.getOrientation());
 }
 
-void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
-    auto& outputState = editState();
-    outputState.layerStackId = layerStackId;
-    outputState.layerStackInternal = isInternal;
-
+void Output::setLayerFilter(ui::LayerFilter filter) {
+    editState().layerFilter = filter;
     dirtyEntireOutput();
 }
 
@@ -371,26 +368,18 @@
     mRenderSurface = std::move(surface);
 }
 
-Region Output::getDirtyRegion(bool repaintEverything) const {
+Region Output::getDirtyRegion() const {
     const auto& outputState = getState();
-    Region dirty(outputState.layerStackSpace.content);
-    if (!repaintEverything) {
-        dirty.andSelf(outputState.dirtyRegion);
-    }
-    return dirty;
+    return outputState.dirtyRegion.intersect(outputState.layerStackSpace.content);
 }
 
-bool Output::belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const {
-    // The layerStackId's must match, and also the layer must not be internal
-    // only when not on an internal output.
-    const auto& outputState = getState();
-    return layerStackId && (*layerStackId == outputState.layerStackId) &&
-            (!internalOnly || outputState.layerStackInternal);
+bool Output::includesLayer(ui::LayerFilter filter) const {
+    return getState().layerFilter.includes(filter);
 }
 
-bool Output::belongsInOutput(const sp<compositionengine::LayerFE>& layerFE) const {
+bool Output::includesLayer(const sp<LayerFE>& layerFE) const {
     const auto* layerFEState = layerFE->getCompositionState();
-    return layerFEState && belongsInOutput(layerFEState->layerStackId, layerFEState->internalOnly);
+    return layerFEState && includesLayer(layerFEState->outputFilter);
 }
 
 std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer(
@@ -496,8 +485,8 @@
         layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
     }
 
-    // Only consider the layers on the given layer stack
-    if (!belongsInOutput(layerFE)) {
+    // Only consider the layers on this output
+    if (!includesLayer(layerFE)) {
         return;
     }
 
@@ -908,7 +897,7 @@
 
 void Output::beginFrame() {
     auto& outputState = editState();
-    const bool dirty = !getDirtyRegion(false).isEmpty();
+    const bool dirty = !getDirtyRegion().isEmpty();
     const bool empty = getOutputLayerCount() == 0;
     const bool wasEmpty = !outputState.lastCompositionHadVisibleLayers;
 
@@ -960,14 +949,9 @@
     }
 
     if (getState().isEnabled) {
-        // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion = getDirtyRegion(refreshArgs.repaintEverything);
-        if (!dirtyRegion.isEmpty()) {
-            base::unique_fd readyFence;
-            // redraw the whole screen
+        if (const auto dirtyRegion = getDirtyRegion(); !dirtyRegion.isEmpty()) {
             static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs));
-
-            mRenderSurface->queueBuffer(std::move(readyFence));
+            mRenderSurface->queueBuffer(base::unique_fd());
         }
     }
 
@@ -1075,13 +1059,9 @@
         clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
     }
 
-    // Note: Updated by generateClientCompositionRequests
-    clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
-
     // Generate the client composition requests for the layers on this output.
     std::vector<LayerFE::LayerSettings> clientCompositionLayers =
             generateClientCompositionRequests(supportsProtectedContent,
-                                              clientCompositionDisplay.clearRegion,
                                               clientCompositionDisplay.outputDataspace);
     appendRegionFlashRequests(debugRegion, clientCompositionLayers);
 
@@ -1126,7 +1106,7 @@
     // bounds its framebuffer cache but Skia RenderEngine has no current policy. The best fix is
     // probably to encapsulate the output buffer into a structure that dispatches resource cleanup
     // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
-    const bool useFramebufferCache = outputState.layerStackInternal;
+    const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay;
     status_t status =
             renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex,
                                     useFramebufferCache, std::move(fd), &readyFence);
@@ -1149,15 +1129,13 @@
 }
 
 std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(
-        bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
+        bool supportsProtectedContent, ui::Dataspace outputDataspace) {
     std::vector<LayerFE::LayerSettings> clientCompositionLayers;
     ALOGV("Rendering client layers");
 
     const auto& outputState = getState();
     const Region viewportRegion(outputState.layerStackSpace.content);
     bool firstLayer = true;
-    // Used when a layer clears part of the buffer.
-    Region stubRegion;
 
     bool disableBlurs = false;
     sp<GraphicBuffer> previousOverrideBuffer = nullptr;
@@ -1219,7 +1197,6 @@
                                                outputState.needsFiltering,
                                        .isSecure = outputState.isSecure,
                                        .supportsProtectedContent = supportsProtectedContent,
-                                       .clearRegion = clientComposition ? clearRegion : stubRegion,
                                        .viewport = outputState.layerStackSpace.content,
                                        .dataspace = outputDataspace,
                                        .realContentIsVisible = realContentIsVisible,
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index ee30ad8..acc9216 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -28,9 +28,7 @@
     dumpVal(out, "usesDeviceComposition", usesDeviceComposition);
     dumpVal(out, "flipClientTarget", flipClientTarget);
     dumpVal(out, "reusedClientComposition", reusedClientComposition);
-
-    dumpVal(out, "layerStack", layerStackId);
-    dumpVal(out, "layerStackInternal", layerStackInternal);
+    dumpVal(out, "layerFilter", layerFilter);
 
     out.append("\n   ");
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index c1cd5ab..ccacdfb 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -171,13 +171,11 @@
             .orientation = orientation,
     };
 
-    Region clearRegion = Region::INVALID_REGION;
     LayerFE::ClientCompositionTargetSettings targetSettings{
             .clip = Region(viewport),
             .needsFiltering = false,
             .isSecure = outputState.isSecure,
             .supportsProtectedContent = false,
-            .clearRegion = clearRegion,
             .viewport = viewport,
             .dataspace = outputDataspace,
             .realContentIsVisible = true,
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index c037cc6..f2978f9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -60,8 +60,7 @@
 constexpr HalVirtualDisplayId HAL_VIRTUAL_DISPLAY_ID{456u};
 constexpr GpuVirtualDisplayId GPU_VIRTUAL_DISPLAY_ID{789u};
 
-const ui::Size DEFAULT_RESOLUTION{1920, 1080};
-constexpr uint32_t DEFAULT_LAYER_STACK = 42;
+constexpr ui::Size DEFAULT_RESOLUTION{1920, 1080};
 
 struct Layer {
     Layer() {
@@ -161,13 +160,11 @@
         EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
     }
 
-    DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
+    DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() {
         return DisplayCreationArgsBuilder()
                 .setId(DEFAULT_DISPLAY_ID)
-                .setConnectionType(ui::DisplayConnectionType::Internal)
                 .setPixels(DEFAULT_RESOLUTION)
                 .setIsSecure(true)
-                .setLayerStackId(DEFAULT_LAYER_STACK)
                 .setPowerAdvisor(&mPowerAdvisor)
                 .build();
     }
@@ -177,7 +174,6 @@
                 .setId(GPU_VIRTUAL_DISPLAY_ID)
                 .setPixels(DEFAULT_RESOLUTION)
                 .setIsSecure(false)
-                .setLayerStackId(DEFAULT_LAYER_STACK)
                 .setPowerAdvisor(&mPowerAdvisor)
                 .build();
     }
@@ -193,14 +189,13 @@
     using Display = DisplayTestCommon::PartialMockDisplay;
     std::shared_ptr<Display> mDisplay =
             createPartialMockDisplay<Display>(mCompositionEngine,
-                                              getDisplayCreationArgsForPhysicalHWCDisplay());
+                                              getDisplayCreationArgsForPhysicalDisplay());
 };
 
 struct FullDisplayImplTestCommon : public DisplayTestCommon {
     using Display = DisplayTestCommon::FullImplDisplay;
     std::shared_ptr<Display> mDisplay =
-            createDisplay<Display>(mCompositionEngine,
-                                   getDisplayCreationArgsForPhysicalHWCDisplay());
+            createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
 };
 
 struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon {
@@ -218,8 +213,7 @@
     LayerNoHWC2Layer mLayer3;
     StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
     std::shared_ptr<Display> mDisplay =
-            createDisplay<Display>(mCompositionEngine,
-                                   getDisplayCreationArgsForPhysicalHWCDisplay());
+            createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
 };
 
 /*
@@ -232,7 +226,7 @@
 
 TEST_F(DisplayCreationTest, createPhysicalInternalDisplay) {
     auto display =
-            impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalHWCDisplay());
+            impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
     EXPECT_TRUE(display->isSecure());
     EXPECT_FALSE(display->isVirtual());
     EXPECT_EQ(DEFAULT_DISPLAY_ID, display->getId());
@@ -252,13 +246,11 @@
 
 using DisplaySetConfigurationTest = PartialMockDisplayTestCommon;
 
-TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) {
+TEST_F(DisplaySetConfigurationTest, configuresPhysicalDisplay) {
     mDisplay->setConfiguration(DisplayCreationArgsBuilder()
                                        .setId(DEFAULT_DISPLAY_ID)
-                                       .setConnectionType(ui::DisplayConnectionType::Internal)
                                        .setPixels(DEFAULT_RESOLUTION)
                                        .setIsSecure(true)
-                                       .setLayerStackId(DEFAULT_LAYER_STACK)
                                        .setPowerAdvisor(&mPowerAdvisor)
                                        .setName(getDisplayNameFromCurrentTest())
                                        .build());
@@ -266,28 +258,11 @@
     EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
     EXPECT_TRUE(mDisplay->isSecure());
     EXPECT_FALSE(mDisplay->isVirtual());
-    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
-    EXPECT_TRUE(mDisplay->getState().layerStackInternal);
     EXPECT_FALSE(mDisplay->isValid());
-}
 
-TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) {
-    mDisplay->setConfiguration(DisplayCreationArgsBuilder()
-                                       .setId(DEFAULT_DISPLAY_ID)
-                                       .setConnectionType(ui::DisplayConnectionType::External)
-                                       .setPixels(DEFAULT_RESOLUTION)
-                                       .setIsSecure(false)
-                                       .setLayerStackId(DEFAULT_LAYER_STACK)
-                                       .setPowerAdvisor(&mPowerAdvisor)
-                                       .setName(getDisplayNameFromCurrentTest())
-                                       .build());
-
-    EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
-    EXPECT_FALSE(mDisplay->isSecure());
-    EXPECT_FALSE(mDisplay->isVirtual());
-    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
-    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
-    EXPECT_FALSE(mDisplay->isValid());
+    const auto& filter = mDisplay->getState().layerFilter;
+    EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack);
+    EXPECT_FALSE(filter.toInternalDisplay);
 }
 
 TEST_F(DisplaySetConfigurationTest, configuresHalVirtualDisplay) {
@@ -295,7 +270,6 @@
                                        .setId(HAL_VIRTUAL_DISPLAY_ID)
                                        .setPixels(DEFAULT_RESOLUTION)
                                        .setIsSecure(false)
-                                       .setLayerStackId(DEFAULT_LAYER_STACK)
                                        .setPowerAdvisor(&mPowerAdvisor)
                                        .setName(getDisplayNameFromCurrentTest())
                                        .build());
@@ -303,9 +277,11 @@
     EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId());
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
-    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
-    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
     EXPECT_FALSE(mDisplay->isValid());
+
+    const auto& filter = mDisplay->getState().layerFilter;
+    EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack);
+    EXPECT_FALSE(filter.toInternalDisplay);
 }
 
 TEST_F(DisplaySetConfigurationTest, configuresGpuVirtualDisplay) {
@@ -313,7 +289,6 @@
                                        .setId(GPU_VIRTUAL_DISPLAY_ID)
                                        .setPixels(DEFAULT_RESOLUTION)
                                        .setIsSecure(false)
-                                       .setLayerStackId(DEFAULT_LAYER_STACK)
                                        .setPowerAdvisor(&mPowerAdvisor)
                                        .setName(getDisplayNameFromCurrentTest())
                                        .build());
@@ -321,9 +296,11 @@
     EXPECT_EQ(GPU_VIRTUAL_DISPLAY_ID, mDisplay->getId());
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
-    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
-    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
     EXPECT_FALSE(mDisplay->isValid());
+
+    const auto& filter = mDisplay->getState().layerFilter;
+    EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack);
+    EXPECT_FALSE(filter.toInternalDisplay);
 }
 
 /*
@@ -902,10 +879,7 @@
     mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
     mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.repaintEverything = false;
-
-    mDisplay->finishFrame(refreshArgs);
+    mDisplay->finishFrame({});
 }
 
 TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
@@ -923,10 +897,7 @@
     gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
     gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.repaintEverything = false;
-
-    gpuDisplay->finishFrame(refreshArgs);
+    gpuDisplay->finishFrame({});
 }
 
 TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
@@ -944,31 +915,7 @@
     gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
     gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
 
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.repaintEverything = false;
-
-    gpuDisplay->finishFrame(refreshArgs);
-}
-
-TEST_F(DisplayFinishFrameTest, performsCompositionIfRepaintEverything) {
-    auto args = getDisplayCreationArgsForGpuVirtualDisplay();
-    std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
-
-    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
-    gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
-
-    // We expect a single call to queueBuffer when composition is not skipped.
-    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
-
-    gpuDisplay->editState().isEnabled = true;
-    gpuDisplay->editState().usesClientComposition = false;
-    gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
-    gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
-
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.repaintEverything = true;
-
-    gpuDisplay->finishFrame(refreshArgs);
+    gpuDisplay->finishFrame({});
 }
 
 /*
@@ -998,10 +945,8 @@
             Display>(mCompositionEngine,
                      DisplayCreationArgsBuilder()
                              .setId(DEFAULT_DISPLAY_ID)
-                             .setConnectionType(ui::DisplayConnectionType::Internal)
                              .setPixels(DEFAULT_RESOLUTION)
                              .setIsSecure(true)
-                             .setLayerStackId(DEFAULT_LAYER_STACK)
                              .setPowerAdvisor(&mPowerAdvisor)
                              .build());
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index ada5adf..224dad1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -49,6 +49,7 @@
     MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
     MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
     MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
+
     MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
     MOCK_METHOD5(getDeviceCompositionChanges,
                  status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
@@ -110,11 +111,15 @@
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
-    MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t));
-    MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>());
-    MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>());
-    MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<PhysicalDisplayId>(hal::HWDisplayId));
-    MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(PhysicalDisplayId));
+
+    MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
+    MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
+    MOCK_METHOD(bool, isHeadless, (), (const, override));
+
+    MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
+                (const, override));
+    MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
+                (const, override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ee73cfc..6c510eb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -403,17 +403,18 @@
 }
 
 /*
- * Output::setLayerStackFilter()
+ * Output::setLayerFilter()
  */
 
-TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
-    const uint32_t layerStack = 123u;
-    mOutput->setLayerStackFilter(layerStack, true);
+TEST_F(OutputTest, setLayerFilterSetsFilterAndDirtiesEntireOutput) {
+    constexpr ui::LayerFilter kFilter{ui::LayerStack{123u}, true};
+    mOutput->setLayerFilter(kFilter);
 
-    EXPECT_TRUE(mOutput->getState().layerStackInternal);
-    EXPECT_EQ(layerStack, mOutput->getState().layerStackId);
+    const auto& state = mOutput->getState();
+    EXPECT_EQ(kFilter.layerStack, state.layerFilter.layerStack);
+    EXPECT_TRUE(state.layerFilter.toInternalDisplay);
 
-    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_THAT(state.dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
 /*
@@ -567,126 +568,100 @@
  * Output::getDirtyRegion()
  */
 
-TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
+TEST_F(OutputTest, getDirtyRegion) {
     const Rect viewport{100, 200};
     mOutput->editState().layerStackSpace.content = viewport;
     mOutput->editState().dirtyRegion.set(50, 300);
 
-    {
-        Region result = mOutput->getDirtyRegion(true);
-
-        EXPECT_THAT(result, RegionEq(Region(viewport)));
-    }
-}
-
-TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
-    const Rect viewport{100, 200};
-    mOutput->editState().layerStackSpace.content = viewport;
-    mOutput->editState().dirtyRegion.set(50, 300);
-
-    {
-        Region result = mOutput->getDirtyRegion(false);
-
-        // The dirtyRegion should be clipped to the display bounds.
-        EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
-    }
+    // The dirty region should be clipped to the display bounds.
+    EXPECT_THAT(mOutput->getDirtyRegion(), RegionEq(Region(Rect(50, 200))));
 }
 
 /*
- * Output::belongsInOutput()
+ * Output::includesLayer()
  */
 
-TEST_F(OutputTest, belongsInOutputFiltersAsExpected) {
-    const uint32_t layerStack1 = 123u;
-    const uint32_t layerStack2 = 456u;
+TEST_F(OutputTest, layerFiltering) {
+    const ui::LayerStack layerStack1{123u};
+    const ui::LayerStack layerStack2{456u};
 
-    // If the output accepts layerStack1 and internal-only layers....
-    mOutput->setLayerStackFilter(layerStack1, true);
+    // If the output is associated to layerStack1 and to an internal display...
+    mOutput->setLayerFilter({layerStack1, true});
 
-    // A layer with no layerStack does not belong to it, internal-only or not.
-    EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, false));
-    EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, true));
+    // It excludes layers with no layer stack, internal-only or not.
+    EXPECT_FALSE(mOutput->includesLayer({ui::INVALID_LAYER_STACK, false}));
+    EXPECT_FALSE(mOutput->includesLayer({ui::INVALID_LAYER_STACK, true}));
 
-    // Any layer with layerStack1 belongs to it, internal-only or not.
-    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
-    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, true));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
+    // It includes layers on layerStack1, internal-only or not.
+    EXPECT_TRUE(mOutput->includesLayer({layerStack1, false}));
+    EXPECT_TRUE(mOutput->includesLayer({layerStack1, true}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack2, true}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack2, false}));
 
-    // If the output accepts layerStack21 but not internal-only layers...
-    mOutput->setLayerStackFilter(layerStack1, false);
+    // If the output is associated to layerStack1 but not to an internal display...
+    mOutput->setLayerFilter({layerStack1, false});
 
-    // Only non-internal layers with layerStack1 belong to it.
-    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack1, true));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
+    // It includes layers on layerStack1, unless they are internal-only.
+    EXPECT_TRUE(mOutput->includesLayer({layerStack1, false}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack1, true}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack2, true}));
+    EXPECT_FALSE(mOutput->includesLayer({layerStack2, false}));
 }
 
-TEST_F(OutputTest, belongsInOutputHandlesLayerWithNoCompositionState) {
+TEST_F(OutputTest, layerFilteringWithoutCompositionState) {
     NonInjectedLayer layer;
     sp<LayerFE> layerFE(layer.layerFE);
 
-    // If the layer has no composition state, it does not belong to any output.
+    // Layers without composition state are excluded.
     EXPECT_CALL(*layer.layerFE, getCompositionState).WillOnce(Return(nullptr));
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 }
 
-TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) {
+TEST_F(OutputTest, layerFilteringWithCompositionState) {
     NonInjectedLayer layer;
     sp<LayerFE> layerFE(layer.layerFE);
 
-    const uint32_t layerStack1 = 123u;
-    const uint32_t layerStack2 = 456u;
+    const ui::LayerStack layerStack1{123u};
+    const ui::LayerStack layerStack2{456u};
 
-    // If the output accepts layerStack1 and internal-only layers....
-    mOutput->setLayerStackFilter(layerStack1, true);
+    // If the output is associated to layerStack1 and to an internal display...
+    mOutput->setLayerFilter({layerStack1, true});
 
-    // A layer with no layerStack does not belong to it, internal-only or not.
-    layer.layerFEState.layerStackId = std::nullopt;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    // It excludes layers with no layer stack, internal-only or not.
+    layer.layerFEState.outputFilter = {ui::INVALID_LAYER_STACK, false};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = std::nullopt;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {ui::INVALID_LAYER_STACK, true};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    // Any layer with layerStack1 belongs to it, internal-only or not.
-    layer.layerFEState.layerStackId = layerStack1;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+    // It includes layers on layerStack1, internal-only or not.
+    layer.layerFEState.outputFilter = {layerStack1, false};
+    EXPECT_TRUE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack1;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack1, true};
+    EXPECT_TRUE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack2;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack2, true};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack2;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack2, false};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    // If the output accepts layerStack1 but not internal-only layers...
-    mOutput->setLayerStackFilter(layerStack1, false);
+    // If the output is associated to layerStack1 but not to an internal display...
+    mOutput->setLayerFilter({layerStack1, false});
 
-    // Only non-internal layers with layerStack1 belong to it.
-    layer.layerFEState.layerStackId = layerStack1;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+    // It includes layers on layerStack1, unless they are internal-only.
+    layer.layerFEState.outputFilter = {layerStack1, false};
+    EXPECT_TRUE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack1;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack1, true};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack2;
-    layer.layerFEState.internalOnly = true;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack2, true};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 
-    layer.layerFEState.layerStackId = layerStack2;
-    layer.layerFEState.internalOnly = false;
-    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+    layer.layerFEState.outputFilter = {layerStack2, false};
+    EXPECT_FALSE(mOutput->includesLayer(layerFE));
 }
 
 /*
@@ -1268,14 +1243,15 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
+        MOCK_METHOD(bool, includesLayer, (const sp<compositionengine::LayerFE>&),
+                    (const, override));
         MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
         MOCK_METHOD2(ensureOutputLayer,
                      compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
     };
 
     OutputEnsureOutputLayerIfVisibleTest() {
-        EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE)))
+        EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE)))
                 .WillRepeatedly(Return(true));
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
@@ -1326,8 +1302,8 @@
 const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
         Region(Rect(0, 0, 200, 100));
 
-TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) {
-    EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
+    EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
     EXPECT_CALL(*mLayer.layerFE,
                 prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry));
 
@@ -1337,8 +1313,8 @@
 }
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest,
-       skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) {
-    EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+       skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerIncluded) {
+    EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
 
     ensureOutputLayerIfVisible();
 }
@@ -2530,7 +2506,7 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+        MOCK_METHOD(Region, getDirtyRegion, (), (const));
     };
 
     OutputBeginFrameTest() {
@@ -2542,8 +2518,7 @@
     struct IfGetDirtyRegionExpectationState
           : public CallOrderStateMachineHelper<TestType, IfGetDirtyRegionExpectationState> {
         [[nodiscard]] auto ifGetDirtyRegionReturns(Region dirtyRegion) {
-            EXPECT_CALL(getInstance()->mOutput, getDirtyRegion(false))
-                    .WillOnce(Return(dirtyRegion));
+            EXPECT_CALL(getInstance()->mOutput, getDirtyRegion()).WillOnce(Return(dirtyRegion));
             return nextState<AndIfGetOutputLayerCountExpectationState>();
         }
     };
@@ -2683,7 +2658,7 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+        MOCK_METHOD(Region, getDirtyRegion, (), (const));
         MOCK_METHOD2(composeSurfaces,
                      std::optional<base::unique_fd>(
                              const Region&, const compositionengine::CompositionRefreshArgs&));
@@ -2711,7 +2686,6 @@
 
 TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) {
     mRefreshArgs.devOptFlashDirtyRegionsDelay = {};
-    mRefreshArgs.repaintEverything = true;
     mOutput.mState.isEnabled = true;
 
     mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2719,7 +2693,6 @@
 
 TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) {
     mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
-    mRefreshArgs.repaintEverything = true;
     mOutput.mState.isEnabled = false;
 
     InSequence seq;
@@ -2729,13 +2702,12 @@
     mOutput.devOptRepaintFlash(mRefreshArgs);
 }
 
-TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) {
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfEnabled) {
     mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
-    mRefreshArgs.repaintEverything = true;
     mOutput.mState.isEnabled = true;
 
     InSequence seq;
-    EXPECT_CALL(mOutput, getDirtyRegion(true)).WillOnce(Return(kEmptyRegion));
+    EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kEmptyRegion));
     EXPECT_CALL(mOutput, postFramebuffer());
     EXPECT_CALL(mOutput, prepareFrame());
 
@@ -2744,11 +2716,10 @@
 
 TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty) {
     mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
-    mRefreshArgs.repaintEverything = false;
     mOutput.mState.isEnabled = true;
 
     InSequence seq;
-    EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion));
+    EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion));
     EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs)));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
     EXPECT_CALL(mOutput, postFramebuffer());
@@ -2992,8 +2963,8 @@
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
         MOCK_CONST_METHOD0(getSkipColorTransform, bool());
-        MOCK_METHOD3(generateClientCompositionRequests,
-                     std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+        MOCK_METHOD2(generateClientCompositionRequests,
+                     std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace));
         MOCK_METHOD2(appendRegionFlashRequests,
                      void(const Region&, std::vector<LayerFE::LayerSettings>&));
         MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -3142,7 +3113,7 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
@@ -3165,7 +3136,7 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(
@@ -3188,14 +3159,13 @@
 
     r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
     r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
-    const constexpr uint32_t kInternalLayerStack = 1234;
-    mOutput.setLayerStackFilter(kInternalLayerStack, true);
+    mOutput.setLayerFilter({ui::LayerStack{1234u}, true});
 
     EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(
@@ -3223,7 +3193,7 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
@@ -3252,7 +3222,7 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
@@ -3281,7 +3251,7 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
             .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
@@ -3316,7 +3286,7 @@
     EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2}))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3}));
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3339,7 +3309,7 @@
     OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
         EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
         EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
-        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
                 .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
         EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
                 .WillRepeatedly(Return());
@@ -3391,7 +3361,7 @@
             .andIfSkipColorTransform(false)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
+                                            kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3402,7 +3372,7 @@
             .andIfSkipColorTransform(false)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
+                                            kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3413,7 +3383,7 @@
             .andIfSkipColorTransform(false)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace,
-                                            kDefaultColorTransformMat, Region::INVALID_REGION,
+                                            kDefaultColorTransformMat,
                                             kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
@@ -3425,7 +3395,7 @@
             .andIfSkipColorTransform(false)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace,
-                                            kDefaultColorTransformMat, Region::INVALID_REGION,
+                                            kDefaultColorTransformMat,
                                             kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
@@ -3438,7 +3408,7 @@
             .andIfSkipColorTransform(true)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
+                                            kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3469,7 +3439,7 @@
 
         EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
 
-        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, _))
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _))
                 .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
         EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
                 .WillRepeatedly(Return());
@@ -3590,7 +3560,7 @@
 TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) {
     mOutput.mState.dataspace = kExpensiveOutputDataspace;
 
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kExpensiveOutputDataspace))
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kExpensiveOutputDataspace))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
 
     // For this test, we also check the call order of key functions.
@@ -3612,7 +3582,7 @@
         EXPECT_CALL(mLayer.outputLayer,
                     writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                     /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
                 .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
         EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
@@ -3652,10 +3622,9 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // compositionengine::Output overrides
         std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
-                bool supportsProtectedContent, Region& clearRegion,
-                ui::Dataspace dataspace) override {
+                bool supportsProtectedContent, ui::Dataspace dataspace) override {
             return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
-                                                                   clearRegion, dataspace);
+                                                                   dataspace);
         }
     };
 
@@ -3739,11 +3708,9 @@
     EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+                                                              kDisplayDataspace);
     EXPECT_EQ(0u, requests.size());
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) {
@@ -3751,11 +3718,9 @@
     mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
     mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+                                                              kDisplayDataspace);
     EXPECT_EQ(0u, requests.size());
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) {
@@ -3770,16 +3735,13 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
                     {mShadowSettings, mLayers[2].mLayerSettings})));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+                                                              kDisplayDataspace);
     ASSERT_EQ(3u, requests.size());
     EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
     EXPECT_EQ(mShadowSettings, requests[1]);
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
 
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
-
     // Check that a timestamp was set for the layers that generated requests
     EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
     EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
@@ -3810,16 +3772,13 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
                     {mShadowSettings, mLayers[2].mLayerSettings})));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+                                                              kDisplayDataspace);
     ASSERT_EQ(3u, requests.size());
     EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
     EXPECT_EQ(mShadowSettings, requests[1]);
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
 
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
-
     // Check that a timestamp was set for the layers that generated requests
     EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
     EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
@@ -3843,13 +3802,10 @@
     EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+                                                              kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
-
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -3869,13 +3825,10 @@
     EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+                                                              kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
-
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) {
@@ -3896,15 +3849,12 @@
     mLayers[0].mLayerFEState.isOpaque = true;
     mLayers[1].mLayerFEState.isOpaque = true;
     mLayers[2].mLayerFEState.isOpaque = true;
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-    Region stubRegion;
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
             false,      /* needs filtering */
             false,      /* secure */
             false,      /* supports protected content */
-            stubRegion, /* clear region */
             kDisplayViewport,
             kDisplayDataspace,
             false /* realContentIsVisible */,
@@ -3916,7 +3866,6 @@
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -3936,15 +3885,13 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
 
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+                                                              kDisplayDataspace);
     ASSERT_EQ(2u, requests.size());
 
     // The second layer is expected to be rendered as alpha=0 black with no blending
     EXPECT_EQ(mBlackoutSettings, requests[0]);
 
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
-
-    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -3953,14 +3900,11 @@
     mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 30, 30));
     mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, 0, 40, 4000));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(Rect(10, 10, 20, 20)),
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -3972,7 +3916,6 @@
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -3984,7 +3927,6 @@
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4001,7 +3943,7 @@
 
     static_cast<void>(
             mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                      accumClearRegion, kDisplayDataspace));
+                                                      kDisplayDataspace));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -4009,14 +3951,11 @@
     mOutput.mState.needsFiltering = false;
     EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4028,7 +3967,6 @@
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4040,7 +3978,6 @@
             false, /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4057,7 +3994,7 @@
 
     static_cast<void>(
             mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                      accumClearRegion, kDisplayDataspace));
+                                                      kDisplayDataspace));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -4065,14 +4002,11 @@
     mOutput.mState.needsFiltering = true;
     EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4085,7 +4019,6 @@
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4097,7 +4030,6 @@
             true,  /* needs filtering */
             false, /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4114,21 +4046,18 @@
 
     static_cast<void>(
             mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                      accumClearRegion, kDisplayDataspace));
+                                                      kDisplayDataspace));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
        wholeOutputSecurityUsedToGenerateRequests) {
     mOutput.mState.isSecure = true;
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4140,7 +4069,6 @@
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4152,7 +4080,6 @@
             false, /* needs filtering */
             true,  /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4169,19 +4096,17 @@
 
     static_cast<void>(
             mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                      accumClearRegion, kDisplayDataspace));
+                                                      kDisplayDataspace));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
        protectedContentSupportUsedToGenerateRequests) {
-    Region accumClearRegion(Rect(10, 11, 12, 13));
 
     compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
             Region(kDisplayFrame),
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4193,7 +4118,6 @@
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4205,7 +4129,6 @@
             false, /* needs filtering */
             false, /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4221,7 +4144,6 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
-                                                                accumClearRegion,
                                                                 kDisplayDataspace));
 }
 
@@ -4336,14 +4258,11 @@
     EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
             .WillRepeatedly(Return(&rightLayer.mOutputLayer));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
-
     compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
             Region(Rect(0, 0, 1000, 1000)),
             false, /* needs filtering */
             true,  /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kPortraitViewport,
             kOutputDataspace,
             true /* realContentIsVisible */,
@@ -4361,7 +4280,6 @@
             false, /* needs filtering */
             true,  /* secure */
             true,  /* supports protected content */
-            accumClearRegion,
             kPortraitViewport,
             kOutputDataspace,
             true /* realContentIsVisible */,
@@ -4375,8 +4293,8 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings})));
 
     constexpr bool supportsProtectedContent = true;
-    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent,
-                                                              accumClearRegion, kOutputDataspace);
+    auto requests =
+            mOutput.generateClientCompositionRequests(supportsProtectedContent, kOutputDataspace);
     ASSERT_EQ(2u, requests.size());
     EXPECT_EQ(leftLayer.mLayerSettings, requests[0]);
     EXPECT_EQ(rightLayer.mLayerSettings, requests[1]);
@@ -4389,13 +4307,11 @@
     const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
     const Region kPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 60, 80));
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
             Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
             false,                                                    /* needs filtering */
             false,                                                    /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             false /* realContentIsVisible */,
@@ -4415,7 +4331,7 @@
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings})));
 
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+                                                              kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
 
     EXPECT_EQ(mShadowSettings, requests[0]);
@@ -4435,13 +4351,11 @@
     mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
     mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
 
-    Region accumClearRegion(Rect(10, 11, 12, 13));
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
             Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
             false,                                                    /* needs filtering */
             false,                                                    /* secure */
             false, /* supports protected content */
-            accumClearRegion,
             kDisplayViewport,
             kDisplayDataspace,
             true /* realContentIsVisible */,
@@ -4456,7 +4370,7 @@
                     {mShadowSettings, mLayers[2].mLayerSettings})));
 
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
-                                                              accumClearRegion, kDisplayDataspace);
+                                                              kDisplayDataspace);
     ASSERT_EQ(2u, requests.size());
 
     EXPECT_EQ(mShadowSettings, requests[0]);
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 5090bb2..431cc93 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -35,7 +35,7 @@
 
 constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u);
+constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(123u);
 const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
 
 using testing::_;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 4445eea..802a17d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -232,7 +232,7 @@
 }
 
 void DisplayDevice::setLayerStack(ui::LayerStack stack) {
-    mCompositionDisplay->setLayerStackFilter(stack, isInternal());
+    mCompositionDisplay->setLayerFilter({stack, isInternal()});
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->setLayerStack(stack);
     }
@@ -349,7 +349,7 @@
 }
 
 ui::LayerStack DisplayDevice::getLayerStack() const {
-    return mCompositionDisplay->getState().layerStackId;
+    return mCompositionDisplay->getState().layerFilter.layerStack;
 }
 
 ui::Transform::RotationFlags DisplayDevice::getTransformHint() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 4d435c7..43a6bd5 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -86,9 +86,7 @@
 
     bool isVirtual() const { return !mConnectionType; }
     bool isPrimary() const { return mIsPrimary; }
-    bool isInternal() const {
-        return !isVirtual() && mConnectionType == ui::DisplayConnectionType::Internal;
-    }
+    bool isInternal() const { return mConnectionType == ui::DisplayConnectionType::Internal; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
@@ -273,7 +271,7 @@
 
     std::atomic<nsecs_t> mLastHwVsync = 0;
 
-    // TODO(b/74619554): Remove special cases for primary display.
+    // TODO(b/182939859): Remove special cases for primary display.
     const bool mIsPrimary;
 
     uint32_t mFlags = 0;
@@ -311,7 +309,7 @@
     int32_t sequenceId = sNextSequenceId++;
     std::optional<Physical> physical;
     sp<IGraphicBufferProducer> surface;
-    ui::LayerStack layerStack = ui::NO_LAYER_STACK;
+    ui::LayerStack layerStack;
     uint32_t flags = 0;
     Rect layerStackSpaceRect;
     Rect orientedDisplaySpaceRect;
@@ -354,16 +352,4 @@
     DisplayModeId activeModeId;
 };
 
-// Predicates for display lookup.
-
-struct WithLayerStack {
-    explicit WithLayerStack(ui::LayerStack layerStack) : layerStack(layerStack) {}
-
-    bool operator()(const DisplayDevice& display) const {
-        return display.getLayerStack() == layerStack;
-    }
-
-    ui::LayerStack layerStack;
-};
-
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index a790b4c..256bca9 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -212,8 +212,6 @@
     RETURN_IF_INVALID_DISPLAY(*displayId, false);
 
     auto& displayData = mDisplayData[*displayId];
-    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
-                 __FUNCTION__, to_string(*displayId).c_str());
 
     {
         // There have been reports of HWCs that signal several vsync events
@@ -271,7 +269,6 @@
     display->setConnected(true);
     auto& displayData = mDisplayData[displayId];
     displayData.hwcDisplay = std::move(display);
-    displayData.isVirtual = true;
     return true;
 }
 
@@ -279,10 +276,8 @@
                                          PhysicalDisplayId displayId) {
     mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
 
-    if (!mInternalHwcDisplayId) {
-        mInternalHwcDisplayId = hwcDisplayId;
-    } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
-        mExternalHwcDisplayId = hwcDisplayId;
+    if (!mPrimaryHwcDisplayId) {
+        mPrimaryHwcDisplayId = hwcDisplayId;
     }
 
     auto& displayData = mDisplayData[displayId];
@@ -372,7 +367,7 @@
     ui::DisplayConnectionType type;
     const auto error = hwcDisplay->getConnectionType(&type);
 
-    const auto FALLBACK_TYPE = hwcDisplay->getId() == mInternalHwcDisplayId
+    const auto FALLBACK_TYPE = hwcDisplay->getId() == mPrimaryHwcDisplayId
             ? ui::DisplayConnectionType::Internal
             : ui::DisplayConnectionType::External;
 
@@ -428,9 +423,6 @@
     RETURN_IF_INVALID_DISPLAY(displayId);
     auto& displayData = mDisplayData[displayId];
 
-    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
-                 __FUNCTION__, to_string(displayId).c_str());
-
     // NOTE: we use our own internal lock here because we have to call
     // into the HWC with the lock held, and we want to make sure
     // that even if HWC blocks (which it shouldn't), it won't
@@ -597,14 +589,11 @@
 status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
-    const auto& displayData = mDisplayData[displayId];
-    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
-                 __FUNCTION__, to_string(displayId).c_str());
-
     if (mode == hal::PowerMode::OFF) {
         setVsyncEnabled(displayId, hal::Vsync::DISABLE);
     }
 
+    const auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
     switch (mode) {
         case hal::PowerMode::OFF:
@@ -678,12 +667,6 @@
     auto& displayData = mDisplayData[displayId];
     const auto hwcDisplayId = displayData.hwcDisplay->getId();
 
-    // TODO(b/74619554): Select internal/external display from remaining displays.
-    if (hwcDisplayId == mInternalHwcDisplayId) {
-        mInternalHwcDisplayId.reset();
-    } else if (hwcDisplayId == mExternalHwcDisplayId) {
-        mExternalHwcDisplayId.reset();
-    }
     mPhysicalDisplayIdMap.erase(hwcDisplayId);
     mDisplayData.erase(displayId);
 }
@@ -693,9 +676,6 @@
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto& displayData = mDisplayData[displayId];
 
-    LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s",
-                 __FUNCTION__, to_string(displayId).c_str());
-
     auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
@@ -853,8 +833,7 @@
 
 std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(
         PhysicalDisplayId displayId) const {
-    if (const auto it = mDisplayData.find(displayId);
-        it != mDisplayData.end() && !it->second.isVirtual) {
+    if (const auto it = mDisplayData.find(displayId); it != mDisplayData.end()) {
         return it->second.hwcDisplay->getId();
     }
     return {};
@@ -868,7 +847,8 @@
         return true;
     }
 
-    if (!mHasMultiDisplaySupport && mInternalHwcDisplayId && mExternalHwcDisplayId) {
+    // Legacy mode only supports IDs LEGACY_DISPLAY_TYPE_PRIMARY and LEGACY_DISPLAY_TYPE_EXTERNAL.
+    if (!mHasMultiDisplaySupport && mPhysicalDisplayIdMap.size() == 2) {
         ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
         return true;
     }
@@ -909,7 +889,7 @@
         }
 
         info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] {
-            const bool isPrimary = !mInternalHwcDisplayId;
+            const bool isPrimary = !mPrimaryHwcDisplayId;
             if (mHasMultiDisplaySupport) {
                 if (const auto info = parseDisplayIdentificationData(port, data)) {
                     return *info;
@@ -922,8 +902,8 @@
             }
 
             return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port),
-                                             .name = isPrimary ? "Internal display"
-                                                               : "External display",
+                                             .name = isPrimary ? "Primary display"
+                                                               : "Secondary display",
                                              .deviceProductInfo = std::nullopt};
         }();
     }
@@ -936,9 +916,12 @@
 
 std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect(
         hal::HWDisplayId hwcDisplayId) {
+    LOG_ALWAYS_FATAL_IF(hwcDisplayId == mPrimaryHwcDisplayId,
+                        "Primary display cannot be disconnected.");
+
     const auto displayId = toPhysicalDisplayId(hwcDisplayId);
     if (!displayId) {
-        ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+        LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
         return {};
     }
 
@@ -947,7 +930,7 @@
     if (isConnected(*displayId)) {
         mDisplayData[*displayId].hwcDisplay->setConnected(false);
     } else {
-        ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+        LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Already disconnected");
     }
     // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
     // via SurfaceFlinger's onHotplugReceived callback handling
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 49f96d9..0a090da 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_HWCOMPOSER_H
-#define ANDROID_SF_HWCOMPOSER_H
+#pragma once
 
 #include <cstdint>
 #include <future>
@@ -228,14 +227,18 @@
     virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
             const = 0;
 
-    // for debugging ----------------------------------------------------------
     virtual void dump(std::string& out) const = 0;
 
     virtual Hwc2::Composer* getComposer() const = 0;
 
-    // TODO(b/74619554): Remove special cases for internal/external display.
-    virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0;
-    virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0;
+    // Returns the first display connected at boot. It cannot be disconnected, which implies an
+    // internal connection type. Its connection via HWComposer::onHotplug, which in practice is
+    // immediately after HWComposer construction, must occur before any call to this function.
+    //
+    // TODO(b/182939859): Remove special cases for primary display.
+    virtual hal::HWDisplayId getPrimaryHwcDisplayId() const = 0;
+    virtual PhysicalDisplayId getPrimaryDisplayId() const = 0;
+    virtual bool isHeadless() const = 0;
 
     virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
     virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
@@ -366,14 +369,19 @@
 
     Hwc2::Composer* getComposer() const override { return mComposer.get(); }
 
-    // TODO(b/74619554): Remove special cases for internal/external display.
-    std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const override {
-        return mInternalHwcDisplayId;
+    hal::HWDisplayId getPrimaryHwcDisplayId() const override {
+        LOG_ALWAYS_FATAL_IF(!mPrimaryHwcDisplayId, "Missing HWC primary display");
+        return *mPrimaryHwcDisplayId;
     }
-    std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const override {
-        return mExternalHwcDisplayId;
+
+    PhysicalDisplayId getPrimaryDisplayId() const override {
+        const auto id = toPhysicalDisplayId(getPrimaryHwcDisplayId());
+        LOG_ALWAYS_FATAL_IF(!id, "Missing primary display");
+        return *id;
     }
 
+    virtual bool isHeadless() const override { return !mPrimaryHwcDisplayId; }
+
     std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const override;
     std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const override;
 
@@ -382,12 +390,9 @@
     friend TestableSurfaceFlinger;
 
     struct DisplayData {
-        bool isVirtual = false;
         std::unique_ptr<HWC2::Display> hwcDisplay;
         sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
-        buffer_handle_t outbufHandle = nullptr;
-        sp<Fence> outbufAcquireFence = Fence::NO_FENCE;
 
         bool validateWasSkipped;
         hal::Error presentError;
@@ -418,8 +423,7 @@
     bool mRegisteredCallback = false;
 
     std::unordered_map<hal::HWDisplayId, PhysicalDisplayId> mPhysicalDisplayIdMap;
-    std::optional<hal::HWDisplayId> mInternalHwcDisplayId;
-    std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
+    std::optional<hal::HWDisplayId> mPrimaryHwcDisplayId;
     bool mHasMultiDisplaySupport = false;
 
     const size_t mMaxVirtualDisplayDimension;
@@ -428,5 +432,3 @@
 
 } // namespace impl
 } // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 140436a..b872c85 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -107,7 +107,7 @@
     mDrawingState.requestedCrop = mDrawingState.crop;
     mDrawingState.z = 0;
     mDrawingState.color.a = 1.0f;
-    mDrawingState.layerStack = 0;
+    mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK;
     mDrawingState.sequence = 0;
     mDrawingState.requested_legacy = mDrawingState.active_legacy;
     mDrawingState.width = UINT32_MAX;
@@ -391,7 +391,6 @@
 
 void Layer::prepareBasicGeometryCompositionState() {
     const auto& drawingState{getDrawingState()};
-    const uint32_t layerStack = getLayerStack();
     const auto alpha = static_cast<float>(getAlpha());
     const bool opaque = isOpaque(drawingState);
     const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
@@ -403,9 +402,7 @@
     }
 
     auto* compositionState = editCompositionState();
-    compositionState->layerStackId =
-            (layerStack != ~0u) ? std::make_optional(layerStack) : std::nullopt;
-    compositionState->internalOnly = getPrimaryDisplayOnly();
+    compositionState->outputFilter = getOutputFilter();
     compositionState->isVisible = isVisible();
     compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
     compositionState->shadowRadius = mEffectiveShadowRadius;
@@ -728,14 +725,14 @@
     mDrawingState.bufferlessSurfaceFramesTX.clear();
 }
 
-uint32_t Layer::getTransactionFlags(uint32_t flags) {
-    auto ret = mTransactionFlags & flags;
-    mTransactionFlags &= ~flags;
-    return ret;
+uint32_t Layer::clearTransactionFlags(uint32_t mask) {
+    const auto flags = mTransactionFlags & mask;
+    mTransactionFlags &= ~mask;
+    return flags;
 }
 
-uint32_t Layer::setTransactionFlags(uint32_t flags) {
-    return mTransactionFlags |= flags;
+void Layer::setTransactionFlags(uint32_t mask) {
+    mTransactionFlags |= mask;
 }
 
 bool Layer::setPosition(float x, float y) {
@@ -1005,7 +1002,7 @@
     return true;
 }
 
-bool Layer::setLayerStack(uint32_t layerStack) {
+bool Layer::setLayerStack(ui::LayerStack layerStack) {
     if (mDrawingState.layerStack == layerStack) return false;
     mDrawingState.sequence++;
     mDrawingState.layerStack = layerStack;
@@ -1052,12 +1049,11 @@
     return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
 };
 
-uint32_t Layer::getLayerStack() const {
-    auto p = mDrawingParent.promote();
-    if (p == nullptr) {
-        return getDrawingState().layerStack;
+ui::LayerStack Layer::getLayerStack() const {
+    if (const auto parent = mDrawingParent.promote()) {
+        return parent->getLayerStack();
     }
-    return p->getLayerStack();
+    return getDrawingState().layerStack;
 }
 
 bool Layer::setShadowRadius(float shadowRadius) {
@@ -1371,7 +1367,7 @@
 
     info.mVisibleRegion = getVisibleRegion(display);
     info.mSurfaceDamageRegion = surfaceDamageRegion;
-    info.mLayerStack = getLayerStack();
+    info.mLayerStack = getLayerStack().id;
     info.mX = ds.transform.tx();
     info.mY = ds.transform.ty();
     info.mZ = ds.z;
@@ -2088,7 +2084,7 @@
         LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
                                        [&]() { return layerInfo->mutable_transparent_region(); });
 
-        layerInfo->set_layer_stack(getLayerStack());
+        layerInfo->set_layer_stack(getLayerStack().id);
         layerInfo->set_z(state.z);
 
         LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(),
@@ -2135,7 +2131,7 @@
     if (traceFlags & SurfaceTracing::TRACE_INPUT) {
         WindowInfo info;
         if (useDrawing) {
-            info = fillInputInfo({nullptr});
+            info = fillInputInfo(nullptr);
         } else {
             info = state.inputInfo;
         }
@@ -2164,7 +2160,7 @@
     return getCroppedBufferSize(getDrawingState());
 }
 
-void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toNonRotatedDisplay) {
+void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) {
     // Transform layer size to screen space and inset it by surface insets.
     // If this is a portal window, set the touchableRegion to the layerBounds.
     Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
@@ -2184,13 +2180,13 @@
         return;
     }
 
-    ui::Transform layerToDisplay = getInputTransform();
+    const ui::Transform layerTransform = getInputTransform();
     // Transform that takes window coordinates to non-rotated display coordinates
-    ui::Transform t = toNonRotatedDisplay * layerToDisplay;
+    ui::Transform t = displayTransform * layerTransform;
     int32_t xSurfaceInset = info.surfaceInset;
     int32_t ySurfaceInset = info.surfaceInset;
-    // Bring screenBounds into non-rotated space
-    Rect screenBounds = toNonRotatedDisplay.transform(Rect{mScreenBounds});
+    // Bring screenBounds into non-unrotated space
+    Rect screenBounds = displayTransform.transform(Rect{mScreenBounds});
 
     const float xScale = t.getScaleX();
     const float yScale = t.getScaleY();
@@ -2269,26 +2265,28 @@
     }
 }
 
-WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
+WindowInfo Layer::fillInputInfo(const DisplayDevice* display) {
     if (!hasInputInfo()) {
         mDrawingState.inputInfo.name = getName();
         mDrawingState.inputInfo.ownerUid = mOwnerUid;
         mDrawingState.inputInfo.ownerPid = mOwnerPid;
         mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL;
         mDrawingState.inputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL;
-        mDrawingState.inputInfo.displayId = getLayerStack();
+        mDrawingState.inputInfo.displayId = getLayerStack().id;
     }
 
     WindowInfo info = mDrawingState.inputInfo;
     info.id = sequence;
-    info.displayId = getLayerStack();
+    info.displayId = getLayerStack().id;
 
-    // Transform that goes from "logical(rotated)" display to the non-rotated display.
-    ui::Transform toNonRotatedDisplay;
+    // Transform that maps from LayerStack space to display space, e.g. rotated to non-rotated.
+    // Used when InputFlinger operates in display space.
+    ui::Transform displayTransform;
     if (display) {
         // The physical orientation is set when the orientation of the display panel is different
-        // than the default orientation of the device. We do not need to expose the physical
-        // orientation of the panel outside of SurfaceFlinger.
+        // than the default orientation of the device. Other services like InputFlinger do not know
+        // about this, so we do not need to expose the physical orientation of the panel outside of
+        // SurfaceFlinger.
         const ui::Rotation inversePhysicalOrientation =
                 ui::ROTATION_0 - display->getPhysicalOrientation();
         auto width = display->getWidth();
@@ -2297,9 +2295,10 @@
             inversePhysicalOrientation == ui::ROTATION_270) {
             std::swap(width, height);
         }
-        const auto rotationFlags = ui::Transform::toRotationFlags(inversePhysicalOrientation);
-        const ui::Transform undoPhysicalOrientation(rotationFlags, width, height);
-        toNonRotatedDisplay = undoPhysicalOrientation * display->getTransform();
+        const ui::Transform undoPhysicalOrientation(ui::Transform::toRotationFlags(
+                                                            inversePhysicalOrientation),
+                                                    width, height);
+        displayTransform = undoPhysicalOrientation * display->getTransform();
 
         // Send the inverse of the display orientation so that input can transform points back to
         // the rotated display space.
@@ -2309,7 +2308,7 @@
         info.displayWidth = width;
         info.displayHeight = height;
     }
-    fillInputFrameInfo(info, toNonRotatedDisplay);
+    fillInputFrameInfo(info, displayTransform);
 
     // For compatibility reasons we let layers which can receive input
     // receive input before they have actually submitted a buffer. Because
@@ -2325,15 +2324,11 @@
 
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
-        if (cropLayer == nullptr) {
-            info.touchableRegion = Region(toNonRotatedDisplay.transform(Rect{mScreenBounds}));
-        } else {
-            info.touchableRegion =
-                    Region(toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
-        }
+        const Rect bounds(cropLayer ? cropLayer->mScreenBounds : mScreenBounds);
+        info.touchableRegion = Region(displayTransform.transform(bounds));
     } else if (cropLayer != nullptr) {
         info.touchableRegion = info.touchableRegion.intersect(
-                toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
+                displayTransform.transform(Rect{cropLayer->mScreenBounds}));
     }
 
     // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
@@ -2344,9 +2339,8 @@
     // 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.
     if (isClone()) {
-        sp<Layer> clonedRoot = getClonedRoot();
-        if (clonedRoot != nullptr) {
-            Rect rect = toNonRotatedDisplay.transform(Rect{clonedRoot->mScreenBounds});
+        if (const sp<Layer> clonedRoot = getClonedRoot()) {
+            const Rect rect = displayTransform.transform(Rect{clonedRoot->mScreenBounds});
             info.touchableRegion = info.touchableRegion.intersect(rect);
         }
     }
@@ -2542,14 +2536,14 @@
     }
 }
 
-bool Layer::getPrimaryDisplayOnly() const {
+bool Layer::isInternalDisplayOverlay() const {
     const State& s(mDrawingState);
     if (s.flags & layer_state_t::eLayerSkipScreenshot) {
         return true;
     }
 
     sp<Layer> parent = mDrawingParent.promote();
-    return parent == nullptr ? false : parent->getPrimaryDisplayOnly();
+    return parent && parent->isInternalDisplayOverlay();
 }
 
 void Layer::setClonedChild(const sp<Layer>& clonedChild) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 470103c..4200be4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -151,12 +151,7 @@
         Geometry requested_legacy;
         int32_t z;
 
-        // The identifier of the layer stack this layer belongs to. A layer can
-        // only be associated to a single layer stack. A layer stack is a
-        // z-ordered group of layers which can be associated to one or more
-        // displays. Using the same layer stack on different displays is a way
-        // to achieve mirroring.
-        uint32_t layerStack;
+        ui::LayerStack layerStack;
 
         uint32_t flags;
         uint8_t reserved[2];
@@ -405,8 +400,8 @@
     virtual bool setTransparentRegionHint(const Region& transparent);
     virtual bool setTrustedOverlay(bool);
     virtual bool setFlags(uint32_t flags, uint32_t mask);
-    virtual bool setLayerStack(uint32_t layerStack);
-    virtual uint32_t getLayerStack() const;
+    virtual bool setLayerStack(ui::LayerStack);
+    virtual ui::LayerStack getLayerStack() const;
     virtual bool setMetadata(const LayerMetadata& data);
     virtual void setChildrenDrawingParent(const sp<Layer>&);
     virtual bool reparent(const sp<IBinder>& newParentHandle);
@@ -645,13 +640,12 @@
     bool isLegacyDataSpace() const;
 
     uint32_t getTransactionFlags() const { return mTransactionFlags; }
-    uint32_t getTransactionFlags(uint32_t flags);
-    uint32_t setTransactionFlags(uint32_t flags);
 
-    // Deprecated, please use compositionengine::Output::belongsInOutput()
-    // instead.
-    // TODO(lpique): Move the remaining callers (screencap) to the new function.
-    bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; }
+    // Sets the masked bits.
+    void setTransactionFlags(uint32_t mask);
+
+    // Clears and returns the masked bits.
+    uint32_t clearTransactionFlags(uint32_t mask);
 
     FloatRect getBounds(const Region& activeTransparentRegion) const;
     FloatRect getBounds() const;
@@ -684,6 +678,14 @@
      */
     bool isHiddenByPolicy() const;
 
+    // True if the layer should be skipped in screenshots, screen recordings,
+    // and mirroring to external or virtual displays.
+    bool isInternalDisplayOverlay() const;
+
+    ui::LayerFilter getOutputFilter() const {
+        return {getLayerStack(), isInternalDisplayOverlay()};
+    }
+
     bool isRemovedFromCurrentState() const;
 
     LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*);
@@ -700,8 +702,6 @@
 
     gui::WindowInfo::Type getWindowType() const { return mWindowType; }
 
-    bool getPrimaryDisplayOnly() const;
-
     void updateMirrorInfo();
 
     /*
@@ -851,7 +851,8 @@
     bool getPremultipledAlpha() const;
     void setInputInfo(const gui::WindowInfo& info);
 
-    gui::WindowInfo fillInputInfo(const sp<DisplayDevice>& display);
+    gui::WindowInfo fillInputInfo(const DisplayDevice*);
+
     /**
      * Returns whether this layer has an explicitly set input-info.
      */
@@ -1072,8 +1073,8 @@
     // hasInputInfo() or no-op if no such parent is found.
     void fillTouchOcclusionMode(gui::WindowInfo& info);
 
-    // Fills in the frame and transform info for the gui::WindowInfo
-    void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& toNonRotatedDisplay);
+    // Fills in the frame and transform info for the gui::WindowInfo.
+    void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& displayTransform);
 
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling, which is
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index aee820a..f52e60d 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -45,8 +45,8 @@
     const auto& lState = l->getDrawingState();
     const auto& rState = r->getDrawingState();
 
-    uint32_t ls = lState.layerStack;
-    uint32_t rs = rState.layerStack;
+    const auto ls = lState.layerStack;
+    const auto rs = rState.layerStack;
     if (ls != rs)
         return (ls > rs) ? 1 : -1;
 
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index bc32a1d..0789e8d 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -25,6 +25,10 @@
 #include "Client.h"
 #include "Layer.h"
 
+#include <SkBlendMode.h>
+#include <SkPaint.h>
+#include <SkRect.h>
+#include <SkSurface.h>
 #include <gui/IProducerListener.h>
 
 #undef LOG_TAG
@@ -32,85 +36,64 @@
 
 namespace android {
 
-void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color,
-                                                      const sp<GraphicBuffer>& buffer,
-                                                      uint8_t* pixels) {
-    for (int32_t j = r.top; j < r.bottom; j++) {
-        if (j >= buffer->getHeight()) {
-            break;
-        }
-
-        for (int32_t i = r.left; i < r.right; i++) {
-            if (i >= buffer->getWidth()) {
-                break;
-            }
-
-            uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j));
-            iter[0] = uint8_t(color.r * 255);
-            iter[1] = uint8_t(color.g * 255);
-            iter[2] = uint8_t(color.b * 255);
-            iter[3] = uint8_t(color.a * 255);
-        }
-    }
-}
-
-void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left,
-                                                         const half4& color,
-                                                         const sp<GraphicBuffer>& buffer,
-                                                         uint8_t* pixels) {
-    const Rect rect = [&]() {
+void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor& color,
+                                                         SkCanvas& canvas) {
+    const SkRect rect = [&]() {
         switch (segment) {
             case Segment::Upper:
-                return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
+                return SkRect::MakeLTRB(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
             case Segment::UpperLeft:
-                return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
+                return SkRect::MakeLTRB(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
             case Segment::UpperRight:
-                return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
-                            DIGIT_HEIGHT / 2);
+                return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
+                                        DIGIT_HEIGHT / 2);
             case Segment::Middle:
-                return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH,
-                            DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
+                return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2,
+                                        left + DIGIT_WIDTH, DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
             case Segment::LowerLeft:
-                return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
+                return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
             case Segment::LowerRight:
-                return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH,
-                            DIGIT_HEIGHT);
-            case Segment::Buttom:
-                return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT);
+                return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2,
+                                        left + DIGIT_WIDTH, DIGIT_HEIGHT);
+            case Segment::Bottom:
+                return SkRect::MakeLTRB(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH,
+                                        DIGIT_HEIGHT);
         }
     }();
 
-    drawRect(rect, color, buffer, pixels);
+    SkPaint paint;
+    paint.setColor(color);
+    paint.setBlendMode(SkBlendMode::kSrc);
+    canvas.drawRect(rect, paint);
 }
 
-void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color,
-                                                       const sp<GraphicBuffer>& buffer,
-                                                       uint8_t* pixels) {
+void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkColor& color,
+                                                       SkCanvas& canvas) {
     if (digit < 0 || digit > 9) return;
 
     if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
         digit == 8 || digit == 9)
-        drawSegment(Segment::Upper, left, color, buffer, pixels);
+        drawSegment(Segment::Upper, left, color, canvas);
     if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
-        drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+        drawSegment(Segment::UpperLeft, left, color, canvas);
     if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
         digit == 8 || digit == 9)
-        drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+        drawSegment(Segment::UpperRight, left, color, canvas);
     if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
         digit == 9)
-        drawSegment(Segment::Middle, left, color, buffer, pixels);
+        drawSegment(Segment::Middle, left, color, canvas);
     if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
-        drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+        drawSegment(Segment::LowerLeft, left, color, canvas);
     if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
         digit == 7 || digit == 8 || digit == 9)
-        drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+        drawSegment(Segment::LowerRight, left, color, canvas);
     if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
         digit == 9)
-        drawSegment(Segment::Buttom, left, color, buffer, pixels);
+        drawSegment(Segment::Bottom, left, color, canvas);
 }
 
-std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(
-        int number, const half4& color, bool showSpinner) {
+std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::draw(
+        int number, SkColor& color, ui::Transform::RotationFlags rotation, bool showSpinner) {
     if (number < 0 || number > 1000) return {};
 
     const auto hundreds = number / 100;
@@ -120,55 +103,76 @@
     std::vector<sp<GraphicBuffer>> buffers;
     const auto loopCount = showSpinner ? 6 : 1;
     for (int i = 0; i < loopCount; i++) {
+        // Pre-rotate the buffer before it reaches SurfaceFlinger.
+        SkMatrix canvasTransform = SkMatrix();
+        auto [bufferWidth, bufferHeight] = [&] {
+            switch (rotation) {
+                case ui::Transform::ROT_90:
+                    canvasTransform.setTranslate(BUFFER_HEIGHT, 0);
+                    canvasTransform.preRotate(90);
+                    return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH);
+                case ui::Transform::ROT_270:
+                    canvasTransform.setRotate(270, BUFFER_WIDTH / 2.0, BUFFER_WIDTH / 2.0);
+                    return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH);
+                default:
+                    return std::make_tuple(BUFFER_WIDTH, BUFFER_HEIGHT);
+            }
+        }();
         sp<GraphicBuffer> buffer =
-                new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                new GraphicBuffer(bufferWidth, bufferHeight, HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                   GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
                                           GRALLOC_USAGE_HW_TEXTURE,
                                   "RefreshRateOverlayBuffer");
         const status_t bufferStatus = buffer->initCheck();
         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
                             bufferStatus);
-        uint8_t* pixels;
-        buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
-        // Clear buffer content
-        drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
+
+        sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight);
+        SkCanvas* canvas = surface->getCanvas();
+        canvas->setMatrix(canvasTransform);
+
         int left = 0;
         if (hundreds != 0) {
-            drawDigit(hundreds, left, color, buffer, pixels);
+            drawDigit(hundreds, left, color, *canvas);
         }
         left += DIGIT_WIDTH + DIGIT_SPACE;
 
         if (tens != 0) {
-            drawDigit(tens, left, color, buffer, pixels);
+            drawDigit(tens, left, color, *canvas);
         }
         left += DIGIT_WIDTH + DIGIT_SPACE;
 
-        drawDigit(ones, left, color, buffer, pixels);
+        drawDigit(ones, left, color, *canvas);
         left += DIGIT_WIDTH + DIGIT_SPACE;
 
         if (showSpinner) {
             switch (i) {
                 case 0:
-                    drawSegment(Segment::Upper, left, color, buffer, pixels);
+                    drawSegment(Segment::Upper, left, color, *canvas);
                     break;
                 case 1:
-                    drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+                    drawSegment(Segment::UpperRight, left, color, *canvas);
                     break;
                 case 2:
-                    drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+                    drawSegment(Segment::LowerRight, left, color, *canvas);
                     break;
                 case 3:
-                    drawSegment(Segment::Buttom, left, color, buffer, pixels);
+                    drawSegment(Segment::Bottom, left, color, *canvas);
                     break;
                 case 4:
-                    drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+                    drawSegment(Segment::LowerLeft, left, color, *canvas);
                     break;
                 case 5:
-                    drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+                    drawSegment(Segment::UpperLeft, left, color, *canvas);
                     break;
             }
         }
 
+        void* pixels = nullptr;
+        buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+        const SkImageInfo& imageInfo = surface->imageInfo();
+        size_t dstRowBytes = buffer->getStride() * imageInfo.bytesPerPixel();
+        canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
         buffer->unlock();
         buffers.emplace_back(buffer);
     }
@@ -214,7 +218,22 @@
 
 const std::vector<std::shared_ptr<renderengine::ExternalTexture>>&
 RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
-    if (mBufferCache.find(fps) == mBufferCache.end()) {
+    ui::Transform::RotationFlags transformHint = mLayer->getTransformHint();
+    // Tell SurfaceFlinger about the pre-rotation on the buffer.
+    const auto transform = [&] {
+        switch (transformHint) {
+            case ui::Transform::ROT_90:
+                return ui::Transform::ROT_270;
+            case ui::Transform::ROT_270:
+                return ui::Transform::ROT_90;
+            default:
+                return ui::Transform::ROT_0;
+        }
+    }();
+    mLayer->setTransform(transform);
+
+    if (mBufferCache.find(transformHint) == mBufferCache.end() ||
+        mBufferCache.at(transformHint).find(fps) == mBufferCache.at(transformHint).end()) {
         // Ensure the range is > 0, so we don't divide by 0.
         const auto rangeLength = std::max(1u, mHighFps - mLowFps);
         // Clip values outside the range [mLowFps, mHighFps]. The current fps may be outside
@@ -222,12 +241,14 @@
         fps = std::max(fps, mLowFps);
         fps = std::min(fps, mHighFps);
         const auto fpsScale = static_cast<float>(fps - mLowFps) / rangeLength;
-        half4 color;
-        color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
-        color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
-        color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
-        color.a = ALPHA;
-        auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner);
+        SkColor4f colorBase = SkColor4f::FromColor(HIGH_FPS_COLOR) * fpsScale;
+        SkColor4f lowFpsColor = SkColor4f::FromColor(LOW_FPS_COLOR) * (1 - fpsScale);
+        colorBase.fR = colorBase.fR + lowFpsColor.fR;
+        colorBase.fG = colorBase.fG + lowFpsColor.fG;
+        colorBase.fB = colorBase.fB + lowFpsColor.fB;
+        colorBase.fA = ALPHA;
+        SkColor color = colorBase.toSkColor();
+        auto buffers = SevenSegmentDrawer::draw(fps, color, transformHint, mShowSpinner);
         std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures;
         std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures),
                        [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> {
@@ -237,10 +258,10 @@
                                                                   renderengine::ExternalTexture::
                                                                           Usage::READABLE);
                        });
-        mBufferCache.emplace(fps, textures);
+        mBufferCache[transformHint].emplace(fps, textures);
     }
 
-    return mBufferCache[fps];
+    return mBufferCache[transformHint][fps];
 }
 
 void RefreshRateOverlay::setViewport(ui::Size viewport) {
@@ -260,7 +281,7 @@
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
-void RefreshRateOverlay::setLayerStack(uint32_t stack) {
+void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
     mLayer->setLayerStack(stack);
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index f9baa89..63ae383 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -18,10 +18,13 @@
 
 #include <math/vec4.h>
 #include <renderengine/RenderEngine.h>
+#include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Size.h>
 #include <utils/StrongPointer.h>
 
+#include <SkCanvas.h>
+#include <SkColor.h>
 #include <unordered_map>
 
 #include "Fps.h"
@@ -39,7 +42,7 @@
 public:
     RefreshRateOverlay(SurfaceFlinger&, uint32_t lowFps, uint32_t highFps, bool showSpinner);
 
-    void setLayerStack(uint32_t stack);
+    void setLayerStack(ui::LayerStack);
     void setViewport(ui::Size);
     void changeRefreshRate(const Fps&);
     void onInvalidate();
@@ -47,20 +50,16 @@
 private:
     class SevenSegmentDrawer {
     public:
-        static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color,
-                                                         bool showSpinner);
+        static std::vector<sp<GraphicBuffer>> draw(int number, SkColor& color,
+                                                   ui::Transform::RotationFlags, bool showSpinner);
         static uint32_t getHeight() { return BUFFER_HEIGHT; }
         static uint32_t getWidth() { return BUFFER_WIDTH; }
 
     private:
-        enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom };
+        enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
 
-        static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer,
-                             uint8_t* pixels);
-        static void drawSegment(Segment segment, int left, const half4& color,
-                                const sp<GraphicBuffer>& buffer, uint8_t* pixels);
-        static void drawDigit(int digit, int left, const half4& color,
-                              const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+        static void drawSegment(Segment segment, int left, SkColor& color, SkCanvas& canvas);
+        static void drawDigit(int digit, int left, SkColor& color, SkCanvas& canvas);
 
         static constexpr uint32_t DIGIT_HEIGHT = 100;
         static constexpr uint32_t DIGIT_WIDTH = 64;
@@ -80,13 +79,15 @@
     sp<IBinder> mIBinder;
     sp<IGraphicBufferProducer> mGbp;
 
-    std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>
+    std::unordered_map<
+            ui::Transform::RotationFlags,
+            std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>>
             mBufferCache;
     std::optional<int> mCurrentFps;
     int mFrame = 0;
     static constexpr float ALPHA = 0.8f;
-    const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
-    const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
+    const SkColor LOW_FPS_COLOR = SK_ColorRED;
+    const SkColor HIGH_FPS_COLOR = SK_ColorGREEN;
 
     const bool mShowSpinner;
 
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
index 9a6c853..5fea521 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -14,10 +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"
-
 #include "RenderArea.h"
 
 namespace android {
@@ -33,6 +29,3 @@
 }
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index c38cd68..e922d46 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -140,6 +140,8 @@
         return 0;
     }
 
+    constexpr float kScoreForFractionalPairs = .8f;
+
     // Slightly prefer seamless switches.
     constexpr float kSeamedSwitchPenalty = 0.95f;
     const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
@@ -156,19 +158,29 @@
     const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
     if (layer.vote == LayerVoteType::ExplicitDefault) {
         // Find the actual rate the layer will render, assuming
-        // that layerPeriod is the minimal time to render a frame
+        // that layerPeriod is the minimal period to render a frame.
+        // For example if layerPeriod is 20ms and displayPeriod is 16ms,
+        // then the actualLayerPeriod will be 32ms, because it is the
+        // smallest multiple of the display period which is >= layerPeriod.
         auto actualLayerPeriod = displayPeriod;
         int multiplier = 1;
         while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
             multiplier++;
             actualLayerPeriod = displayPeriod * multiplier;
         }
+
+        // Because of the threshold we used above it's possible that score is slightly
+        // above 1.
         return std::min(1.0f,
                         static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
     }
 
     if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
         layer.vote == LayerVoteType::Heuristic) {
+        if (isFractionalPairOrMultiple(refreshRate.getFps(), layer.desiredRefreshRate)) {
+            return kScoreForFractionalPairs * seamlessness;
+        }
+
         // Calculate how many display vsyncs we need to present a single frame for this
         // layer
         const auto [displayFramesQuotient, displayFramesRemainder] =
@@ -421,7 +433,7 @@
 
             const auto layerScore =
                     calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
-            ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+            ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
                   scores[i].refreshRate->getName().c_str(), layerScore);
             scores[i].score += weight * layerScore;
         }
@@ -582,7 +594,7 @@
 
 template <typename Iter>
 const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
-    constexpr auto EPSILON = 0.001f;
+    constexpr auto kEpsilon = 0.0001f;
     const RefreshRate* bestRefreshRate = begin->refreshRate;
     float max = begin->score;
     for (auto i = begin; i != end; ++i) {
@@ -591,7 +603,7 @@
 
         ATRACE_INT(refreshRate->getName().c_str(), round<int>(score * 100));
 
-        if (score > max * (1 + EPSILON)) {
+        if (score > max * (1 + kEpsilon)) {
             max = score;
             bestRefreshRate = refreshRate;
         }
@@ -910,7 +922,10 @@
 int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) {
     // This calculation needs to be in sync with the java code
     // in DisplayManagerService.getDisplayInfoForFrameRateOverride
-    constexpr float kThreshold = 0.1f;
+
+    // The threshold must be smaller than 0.001 in order to differentiate
+    // between the fractional pairs (e.g. 59.94 and 60).
+    constexpr float kThreshold = 0.0009f;
     const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
     const auto numPeriodsRounded = std::round(numPeriods);
     if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
@@ -920,6 +935,17 @@
     return static_cast<int>(numPeriodsRounded);
 }
 
+bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
+    if (smaller.getValue() > bigger.getValue()) {
+        return isFractionalPairOrMultiple(bigger, smaller);
+    }
+
+    const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
+    constexpr float kCoef = 1000.f / 1001.f;
+    return bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier / kCoef)) ||
+            bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier * kCoef));
+}
+
 void RefreshRateConfigs::dump(std::string& result) const {
     std::lock_guard lock(mLock);
     base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n",
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 4a9a1fd..0d75689 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -335,6 +335,10 @@
     // layer refresh rate.
     static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
 
+    // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000
+    // for an integer t.
+    static bool isFractionalPairOrMultiple(Fps, Fps);
+
     using UidToFrameRateOverride = std::map<uid_t, Fps>;
     // Returns the frame rate override for each uid.
     //
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f808981..534efee 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -669,7 +669,7 @@
     }
 }
 
-void Scheduler::notifyTouchEvent() {
+void Scheduler::onTouchHint() {
     if (mTouchTimer) {
         mTouchTimer->reset();
 
@@ -878,7 +878,7 @@
 
 void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
     if (timeline.refreshRequired) {
-        mSchedulerCallback.repaintEverythingForHWC();
+        mSchedulerCallback.scheduleRefresh(FrameHint::kNone);
     }
 
     std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
@@ -891,21 +891,21 @@
 }
 
 void Scheduler::onDisplayRefreshed(nsecs_t timestamp) {
-    bool callRepaint = false;
-    {
+    const bool refresh = [=] {
         std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
         if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
-            if (mLastVsyncPeriodChangeTimeline->refreshTimeNanos < timestamp) {
-                mLastVsyncPeriodChangeTimeline->refreshRequired = false;
-            } else {
-                // We need to send another refresh as refreshTimeNanos is still in the future
-                callRepaint = true;
+            if (timestamp < mLastVsyncPeriodChangeTimeline->refreshTimeNanos) {
+                // We need to schedule another refresh as refreshTimeNanos is still in the future.
+                return true;
             }
-        }
-    }
 
-    if (callRepaint) {
-        mSchedulerCallback.repaintEverythingForHWC();
+            mLastVsyncPeriodChangeTimeline->refreshRequired = false;
+        }
+        return false;
+    }();
+
+    if (refresh) {
+        mSchedulerCallback.scheduleRefresh(FrameHint::kNone);
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4b6905b..420ba61 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -56,10 +56,13 @@
 } // namespace frametimeline
 
 struct ISchedulerCallback {
+    // Indicates frame activity, i.e. whether commit and/or composite is taking place.
+    enum class FrameHint { kNone, kActive };
+
+    virtual void scheduleRefresh(FrameHint) = 0;
     virtual void setVsyncEnabled(bool) = 0;
     virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
                                    scheduler::RefreshRateConfigEvent) = 0;
-    virtual void repaintEverythingForHWC() = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
     virtual void triggerOnFrameRateOverridesChanged() = 0;
 
@@ -136,8 +139,8 @@
     bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
     void resetIdleTimer();
 
-    // Function that resets the touch timer.
-    void notifyTouchEvent();
+    // Indicates that touch interaction is taking place.
+    void onTouchHint();
 
     void setDisplayPowerState(bool normal);
 
@@ -194,6 +197,8 @@
 private:
     friend class TestableScheduler;
 
+    using FrameHint = ISchedulerCallback::FrameHint;
+
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
     enum class ContentDetectionState { Off, On };
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0f7fada..5d8d79b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -415,10 +415,7 @@
     property_get("ro.build.type", value, "user");
     mIsUserBuild = strcmp(value, "user") == 0;
 
-    property_get("debug.sf.showupdates", value, "0");
-    mDebugRegion = atoi(value);
-
-    ALOGI_IF(mDebugRegion, "showupdates enabled");
+    mDebugFlashDelay = base::GetUintProperty("debug.sf.showupdates"s, 0u);
 
     // DDMS debugging deprecated (b/120782499)
     property_get("debug.sf.ddms", value, "0");
@@ -618,17 +615,14 @@
 }
 
 std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
-    const auto internalDisplayId = getInternalDisplayIdLocked();
-    if (!internalDisplayId) {
-        return {};
-    }
-
     std::vector<PhysicalDisplayId> displayIds;
     displayIds.reserve(mPhysicalDisplayTokens.size());
-    displayIds.push_back(*internalDisplayId);
+
+    const auto internalDisplayId = getInternalDisplayIdLocked();
+    displayIds.push_back(internalDisplayId);
 
     for (const auto& [id, token] : mPhysicalDisplayTokens) {
-        if (id != *internalDisplayId) {
+        if (id != internalDisplayId) {
             displayIds.push_back(id);
         }
     }
@@ -795,10 +789,10 @@
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
-    LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
+    LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback.");
     const auto displayId = display->getPhysicalId();
     LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
-                        "Internal display is disconnected.");
+                        "Primary display is disconnected.");
 
     // initialize our drawing state
     mDrawingState = mCurrentState;
@@ -967,6 +961,11 @@
         return NAME_NOT_FOUND;
     }
 
+    const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+    if (!displayId) {
+        return INVALID_OPERATION;
+    }
+
     info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
 
     const auto& supportedModes = display->getSupportedModes();
@@ -1026,18 +1025,18 @@
     }
 
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
-    const auto displayId = display->getPhysicalId();
-    info->supportedColorModes = getDisplayColorModes(displayId);
-
+    info->supportedColorModes = getDisplayColorModes(*display);
     info->hdrCapabilities = display->getHdrCapabilities();
+
     info->autoLowLatencyModeSupported =
-            getHwComposer().hasDisplayCapability(displayId,
+            getHwComposer().hasDisplayCapability(*displayId,
                                                  hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
     std::vector<hal::ContentType> types;
-    getHwComposer().getSupportedContentTypes(displayId, &types);
+    getHwComposer().getSupportedContentTypes(*displayId, &types);
     info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) {
         return type == hal::ContentType::GAME;
     });
+
     return NO_ERROR;
 }
 
@@ -1064,8 +1063,8 @@
     }
 
     if (display->setDesiredActiveMode(info)) {
-        // This will trigger HWC refresh without resetting the idle timer.
-        repaintEverythingForHWC();
+        scheduleRefresh(FrameHint::kNone);
+
         // Start receiving vsync samples now, so that we can detect a period
         // switch.
         mScheduler->resyncToHardwareVsync(true, info.mode->getVsyncPeriod());
@@ -1261,15 +1260,15 @@
     }).wait();
 }
 
-std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) {
-    auto modes = getHwComposer().getColorModes(displayId);
-    bool isInternalDisplay = displayId == getInternalDisplayIdLocked();
+std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) {
+    auto modes = getHwComposer().getColorModes(display.getPhysicalId());
 
-    // If it's built-in display and the configuration claims it's not wide color capable,
+    // If the display is internal and the configuration claims it's not wide color capable,
     // filter out all wide color modes. The typical reason why this happens is that the
     // hardware is not good enough to support GPU composition of wide color, and thus the
     // OEMs choose to disable this capability.
-    if (isInternalDisplay && !hasWideColorDisplay) {
+    if (display.getConnectionType() == ui::DisplayConnectionType::Internal &&
+        !hasWideColorDisplay) {
         const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode);
         modes.erase(newEnd, modes.end());
     }
@@ -1293,34 +1292,41 @@
 }
 
 status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
-    schedule([=]() MAIN_THREAD {
-        const auto displayId = getPhysicalDisplayIdLocked(displayToken);
-        if (!displayId) {
-            ALOGE("Invalid display token %p", displayToken.get());
-            return;
-        }
-        const auto modes = getDisplayColorModes(*displayId);
-        bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
-        if (mode < ColorMode::NATIVE || !exists) {
-            ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
-                  decodeColorMode(mode).c_str(), mode, displayToken.get());
-            return;
-        }
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    auto future = schedule([=]() MAIN_THREAD -> status_t {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
                   decodeColorMode(mode).c_str(), mode, displayToken.get());
-        } else if (display->isVirtual()) {
+            return NAME_NOT_FOUND;
+        }
+
+        if (display->isVirtual()) {
             ALOGW("Attempt to set active color mode %s (%d) for virtual display",
                   decodeColorMode(mode).c_str(), mode);
-        } else {
-            display->getCompositionDisplay()->setColorProfile(
-                    compositionengine::Output::ColorProfile{mode, Dataspace::UNKNOWN,
-                                                            RenderIntent::COLORIMETRIC,
-                                                            Dataspace::UNKNOWN});
+            return INVALID_OPERATION;
         }
-    }).wait();
 
+        const auto modes = getDisplayColorModes(*display);
+        const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end();
+
+        if (mode < ColorMode::NATIVE || !exists) {
+            ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
+                  decodeColorMode(mode).c_str(), mode, displayToken.get());
+            return BAD_VALUE;
+        }
+
+        display->getCompositionDisplay()->setColorProfile(
+                {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN});
+
+        return NO_ERROR;
+    });
+
+    // TODO(b/195698395): Propagate error.
+    future.wait();
     return NO_ERROR;
 }
 
@@ -1640,7 +1646,7 @@
     Boost powerBoost = static_cast<Boost>(boostId);
 
     if (powerBoost == Boost::INTERACTION) {
-        mScheduler->notifyTouchEvent();
+        mScheduler->onTouchHint();
     }
 
     return NO_ERROR;
@@ -1657,16 +1663,26 @@
     return mScheduler->createDisplayEventConnection(handle, eventRegistration);
 }
 
-void SurfaceFlinger::signalTransaction() {
-    mScheduler->resetIdleTimer();
+void SurfaceFlinger::scheduleInvalidate(FrameHint hint) {
+    if (hint == FrameHint::kActive) {
+        mScheduler->resetIdleTimer();
+    }
     mPowerAdvisor.notifyDisplayUpdateImminent();
     mEventQueue->invalidate();
 }
 
+void SurfaceFlinger::scheduleRefresh(FrameHint hint) {
+    mForceRefresh = true;
+    scheduleInvalidate(hint);
+}
+
+void SurfaceFlinger::scheduleRepaint() {
+    mGeometryDirty = true;
+    scheduleRefresh(FrameHint::kActive);
+}
+
 void SurfaceFlinger::signalLayerUpdate() {
-    mScheduler->resetIdleTimer();
-    mPowerAdvisor.notifyDisplayUpdateImminent();
-    mEventQueue->invalidate();
+    scheduleInvalidate(FrameHint::kActive);
 }
 
 void SurfaceFlinger::signalRefresh() {
@@ -1737,8 +1753,8 @@
 
 void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
                                           hal::Connection connection) {
-    ALOGI("%s(%" PRIu64 ", %s)", __func__, hwcDisplayId,
-          connection == hal::Connection::CONNECTED ? "connected" : "disconnected");
+    const bool connected = connection == hal::Connection::CONNECTED;
+    ALOGI("%s HAL display %" PRIu64, connected ? "Connecting" : "Disconnecting", hwcDisplayId);
 
     // Only lock if we're not on the main thread. This function is normally
     // called on a hwbinder thread, but for the primary display it's called on
@@ -1769,7 +1785,7 @@
 
 void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) {
     Mutex::Autolock lock(mStateLock);
-    repaintEverythingForHWC();
+    scheduleRefresh(FrameHint::kNone);
 }
 
 void SurfaceFlinger::setVsyncEnabled(bool enabled) {
@@ -1930,17 +1946,13 @@
     }
 
     if (mRefreshRateOverlaySpinner) {
-        if (Mutex::Autolock lock(mStateLock);
-            const auto display = getDefaultDisplayDeviceLocked()) {
-            if (display) {
-                display->onInvalidate();
-            } else {
-                ALOGW("%s: default display is null", __func__);
-            }
+        Mutex::Autolock lock(mStateLock);
+        if (const auto display = getDefaultDisplayDeviceLocked()) {
+            display->onInvalidate();
         }
     }
 
-    bool refreshNeeded;
+    bool refreshNeeded = mForceRefresh.exchange(false);
     {
         mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) ||
                 mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) ||
@@ -1950,8 +1962,9 @@
 
         mFrameTimeline->setSfWakeUp(vsyncId, frameStart, Fps::fromPeriodNsecs(stats.vsyncPeriod));
 
-        refreshNeeded = handleMessageTransaction();
+        refreshNeeded |= flushAndCommitTransactions();
         refreshNeeded |= handleMessageInvalidate();
+
         if (tracePreComposition) {
             if (mVisibleRegionsDirty) {
                 mTracing.notifyLocked("visibleRegionsDirty");
@@ -1973,7 +1986,6 @@
     updateCursorAsync();
     updateInputFlinger();
 
-    refreshNeeded |= mRepaintEverything;
     if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
         // Signal a refresh if a transaction modified the window state,
         // a new buffer was latched, or if HWC has requested a full
@@ -1995,25 +2007,23 @@
     notifyRegionSamplingThread();
 }
 
-bool SurfaceFlinger::handleMessageTransaction() {
+bool SurfaceFlinger::flushAndCommitTransactions() {
     ATRACE_CALL();
 
-    if (getTransactionFlags(eTransactionFlushNeeded)) {
+    if (clearTransactionFlags(eTransactionFlushNeeded)) {
         flushTransactionQueues();
     }
-    uint32_t transactionFlags = peekTransactionFlags();
-    bool runHandleTransaction =
-            ((transactionFlags & (~eTransactionFlushNeeded)) != 0) || mForceTraversal;
 
-    if (runHandleTransaction) {
-        handleTransaction(eTransactionMask);
+    const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || mForceTraversal;
+    if (shouldCommit) {
+        commitTransactions();
     }
 
     if (transactionFlushNeeded()) {
         setTransactionFlags(eTransactionFlushNeeded);
     }
 
-    return runHandleTransaction;
+    return shouldCommit;
 }
 
 void SurfaceFlinger::onMessageRefresh() {
@@ -2037,7 +2047,6 @@
             refreshArgs.layersWithQueuedFrames.push_back(layerFE);
     }
 
-    refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
     refreshArgs.outputColorSetting = useColorManagement
             ? mDisplayColorSetting
             : compositionengine::OutputColorSetting::kUnmanaged;
@@ -2045,7 +2054,7 @@
     refreshArgs.forceOutputColorMode = mForceColorMode;
 
     refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
-    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
+    refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
     refreshArgs.blursAreExpensive = mBlursAreExpensive;
     refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
 
@@ -2054,11 +2063,11 @@
         mDrawingState.colorMatrixChanged = false;
     }
 
-    refreshArgs.devOptForceClientComposition = mDebugDisableHWC || mDebugRegion;
+    refreshArgs.devOptForceClientComposition = mDebugDisableHWC;
 
-    if (mDebugRegion != 0) {
-        refreshArgs.devOptFlashDirtyRegionsDelay =
-                std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
+    if (mDebugFlashDelay != 0) {
+        refreshArgs.devOptForceClientComposition = true;
+        refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
     }
 
     const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
@@ -2067,8 +2076,6 @@
     refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
     refreshArgs.nextInvalidateTime = mEventQueue->nextExpectedInvalidate();
 
-    mGeometryInvalid = false;
-
     // Store the present time just before calling to the composition engine so we could notify
     // the scheduler.
     const auto presentTime = systemTime();
@@ -2290,7 +2297,7 @@
             int32_t maxArea = 0;
             mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
                 const auto layerFe = layer->getCompositionEngineLayerFE();
-                if (layer->isVisible() && compositionDisplay->belongsInOutput(layerFe)) {
+                if (layer->isVisible() && compositionDisplay->includesLayer(layerFe)) {
                     const Dataspace transfer =
                         static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK);
                     const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
@@ -2421,8 +2428,8 @@
         const auto& displayDevice = pair.second;
         const auto display = displayDevice->getCompositionDisplay();
         for (const auto& layer : mDrawingState.layersSortedByZ) {
-            // only consider the layers on the given layer stack
-            if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+            // Only consider the layers on this display.
+            if (!display->includesLayer(layer->getOutputFilter())) {
                 continue;
             }
 
@@ -2442,29 +2449,26 @@
     }
 }
 
-void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) {
+void SurfaceFlinger::commitTransactions() {
     ATRACE_CALL();
 
-    // here we keep a copy of the drawing state (that is the state that's
-    // going to be overwritten by handleTransactionLocked()) outside of
-    // mStateLock so that the side-effects of the State assignment
-    // don't happen with mStateLock held (which can cause deadlocks).
+    // Keep a copy of the drawing state (that is going to be overwritten
+    // by commitTransactionsLocked) outside of mStateLock so that the side
+    // effects of the State assignment don't happen with mStateLock held,
+    // which can cause deadlocks.
     State drawingState(mDrawingState);
 
-    Mutex::Autolock _l(mStateLock);
+    Mutex::Autolock lock(mStateLock);
     mDebugInTransaction = systemTime();
 
     // Here we're guaranteed that some transaction flags are set
-    // so we can call handleTransactionLocked() unconditionally.
-    // We call getTransactionFlags(), which will also clear the flags,
-    // with mStateLock held to guarantee that mCurrentState won't change
-    // until the transaction is committed.
+    // so we can call commitTransactionsLocked unconditionally.
+    // We clear the flags with mStateLock held to guarantee that
+    // mCurrentState won't change until the transaction is committed.
     modulateVsync(&VsyncModulator::onTransactionCommit);
-    transactionFlags = getTransactionFlags(eTransactionMask);
-    handleTransactionLocked(transactionFlags);
+    commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
 
     mDebugInTransaction = 0;
-    // here the transaction has been committed
 }
 
 void SurfaceFlinger::loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
@@ -2727,14 +2731,12 @@
     compositionengine::DisplayCreationArgsBuilder builder;
     if (const auto& physical = state.physical) {
         builder.setId(physical->id);
-        builder.setConnectionType(physical->type);
     } else {
         builder.setId(acquireVirtualDisplay(resolution, pixelFormat));
     }
 
     builder.setPixels(resolution);
     builder.setIsSecure(state.isSecure);
-    builder.setLayerStackId(state.layerStack);
     builder.setPowerAdvisor(&mPowerAdvisor);
     builder.setName(state.displayName);
     auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -2840,7 +2842,7 @@
             setPowerModeInternal(display, hal::PowerMode::ON);
 
             // TODO(b/175678251) Call a listener instead.
-            if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+            if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
                 updateInternalDisplayVsyncLocked(display);
             }
         }
@@ -2920,8 +2922,8 @@
     mDrawingState.displays = mCurrentState.displays;
 }
 
-void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
-    // Commit display transactions
+void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) {
+    // Commit display transactions.
     const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
     if (displayTransactionNeeded) {
         processDisplayChangesLocked();
@@ -2935,18 +2937,9 @@
         mSomeChildrenChanged = false;
     }
 
-    // Update transform hint
+    // Update transform hint.
     if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
-        // The transform hint might have changed for some layers
-        // (either because a display has changed, or because a layer
-        // as changed).
-        //
-        // Walk through all the layers in currentLayers,
-        // and update their transform hint.
-        //
-        // If a layer is visible only on a single display, then that
-        // display is used to calculate the hint, otherwise we use the
-        // default display.
+        // Layers and/or displays have changed, so update the transform hint for each layer.
         //
         // NOTE: we do this here, rather than when presenting the display so that
         // the hint is set before we acquire a buffer from the surface texture.
@@ -2957,30 +2950,29 @@
         // (soon to become the drawing state list).
         //
         sp<const DisplayDevice> hintDisplay;
-        uint32_t currentlayerStack = 0;
-        bool first = true;
+        ui::LayerStack layerStack;
+
         mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) {
             // NOTE: we rely on the fact that layers are sorted by
             // layerStack first (so we don't have to traverse the list
             // of displays for every layer).
-            uint32_t layerStack = layer->getLayerStack();
-            if (first || currentlayerStack != layerStack) {
-                currentlayerStack = layerStack;
-                // figure out if this layerstack is mirrored
-                // (more than one display) if so, pick the default display,
-                // if not, pick the only display it's on.
+            if (const auto filter = layer->getOutputFilter(); layerStack != filter.layerStack) {
+                layerStack = filter.layerStack;
                 hintDisplay = nullptr;
+
+                // Find the display that includes the layer.
                 for (const auto& [token, display] : mDisplays) {
-                    if (display->getCompositionDisplay()
-                                ->belongsInOutput(layer->getLayerStack(),
-                                                  layer->getPrimaryDisplayOnly())) {
-                        if (hintDisplay) {
-                            hintDisplay = nullptr;
-                            break;
-                        } else {
-                            hintDisplay = display;
-                        }
+                    if (!display->getCompositionDisplay()->includesLayer(filter)) {
+                        continue;
                     }
+
+                    // Pick the primary display if another display mirrors the layer.
+                    if (hintDisplay) {
+                        hintDisplay = nullptr;
+                        break;
+                    }
+
+                    hintDisplay = display;
                 }
             }
 
@@ -2994,20 +2986,10 @@
                 hintDisplay = getDefaultDisplayDeviceLocked();
             }
 
-            // could be null if there is no display available at all to get
-            // the transform hint from.
-            if (hintDisplay) {
-                layer->updateTransformHint(hintDisplay->getTransformHint());
-            }
-
-            first = false;
+            layer->updateTransformHint(hintDisplay->getTransformHint());
         });
     }
 
-    /*
-     * Perform our own transaction if needed
-     */
-
     if (mLayersAdded) {
         mLayersAdded = false;
         // Layers have been added.
@@ -3029,7 +3011,9 @@
         });
     }
 
-    commitTransaction();
+    doCommitTransactions();
+    signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
+    mAnimTransactionPending = false;
 }
 
 void SurfaceFlinger::updateInputFlinger() {
@@ -3064,9 +3048,12 @@
 
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         if (!layer->needsInputInfo()) return;
-        sp<DisplayDevice> display = enablePerWindowInputRotation()
-                ? ON_MAIN_THREAD(getDisplayWithInputByLayer(layer))
-                : nullptr;
+
+        const DisplayDevice* display = nullptr;
+        if (enablePerWindowInputRotation()) {
+            display = ON_MAIN_THREAD(getDisplayWithInputByLayer(layer)).get();
+        }
+
         // When calculating the screen bounds we ignore the transparent region since it may
         // result in an unwanted offset.
         windowInfos.push_back(layer->fillInputInfo(display));
@@ -3175,14 +3162,9 @@
     mEventQueue->setDuration(config.sfWorkDuration);
 }
 
-void SurfaceFlinger::commitTransaction() {
+void SurfaceFlinger::doCommitTransactions() {
     ATRACE_CALL();
-    commitTransactionLocked();
-    signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
-    mAnimTransactionPending = false;
-}
 
-void SurfaceFlinger::commitTransactionLocked() {
     if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
         for (const auto& l : mLayersPendingRemoval) {
@@ -3226,11 +3208,10 @@
 void SurfaceFlinger::commitOffscreenLayers() {
     for (Layer* offscreenLayer : mOffscreenLayers) {
         offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
-            uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
-            if (!trFlags) return;
-
-            layer->doTransaction(0);
-            layer->commitChildList();
+            if (layer->clearTransactionFlags(eTransactionNeeded)) {
+                layer->doTransaction(0);
+                layer->commitChildList();
+            }
         });
     }
 }
@@ -3238,7 +3219,7 @@
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
     for (const auto& [token, displayDevice] : ON_MAIN_THREAD(mDisplays)) {
         auto display = displayDevice->getCompositionDisplay();
-        if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+        if (display->includesLayer(layer->getOutputFilter())) {
             display->editState().dirtyRegion.orSelf(dirty);
         }
     }
@@ -3266,14 +3247,14 @@
     // Display is now waiting on Layer 1's frame, which is behind layer 0's
     // second frame. But layer 0's second frame could be waiting on display.
     mDrawingState.traverse([&](Layer* layer) {
-         uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
-         if (trFlags || mForceTransactionDisplayChange) {
-             const uint32_t flags = layer->doTransaction(0);
-             if (flags & Layer::eVisibleRegion)
-                 mVisibleRegionsDirty = true;
-         }
+        if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) {
+            const uint32_t flags = layer->doTransaction(0);
+            if (flags & Layer::eVisibleRegion) {
+                mVisibleRegionsDirty = true;
+            }
+        }
 
-         if (layer->hasReadyFrame()) {
+        if (layer->hasReadyFrame()) {
             frameQueued = true;
             if (layer->shouldPresentNow(expectedPresentTime)) {
                 mLayersWithQueuedFrames.emplace(layer);
@@ -3281,7 +3262,7 @@
                 ATRACE_NAME("!layer->shouldPresentNow()");
                 layer->useEmptyDamage();
             }
-         } else {
+        } else {
             layer->useEmptyDamage();
         }
     });
@@ -3334,10 +3315,6 @@
     return !mLayersWithQueuedFrames.empty() && newDataLatched;
 }
 
-void SurfaceFlinger::invalidateHwcGeometry() {
-    mGeometryInvalid = true;
-}
-
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
                                         const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
                                         const sp<IBinder>& parentHandle,
@@ -3384,23 +3361,23 @@
     }));
 }
 
-uint32_t SurfaceFlinger::peekTransactionFlags() {
+uint32_t SurfaceFlinger::getTransactionFlags() const {
     return mTransactionFlags;
 }
 
-uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) {
-    return mTransactionFlags.fetch_and(~flags) & flags;
+uint32_t SurfaceFlinger::clearTransactionFlags(uint32_t mask) {
+    return mTransactionFlags.fetch_and(~mask) & mask;
 }
 
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
-    return setTransactionFlags(flags, TransactionSchedule::Late);
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask) {
+    return setTransactionFlags(mask, TransactionSchedule::Late);
 }
 
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule,
-                                             const sp<IBinder>& token) {
-    uint32_t old = mTransactionFlags.fetch_or(flags);
-    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, token);
-    if ((old & flags) == 0) signalTransaction();
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
+                                             const sp<IBinder>& applyToken) {
+    const uint32_t old = mTransactionFlags.fetch_or(mask);
+    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
+    if ((old & mask) == 0) scheduleInvalidate(FrameHint::kActive);
     return old;
 }
 
@@ -4459,7 +4436,7 @@
     d.what = DisplayState::eDisplayProjectionChanged |
              DisplayState::eLayerStackChanged;
     d.token = token;
-    d.layerStack = 0;
+    d.layerStack = ui::DEFAULT_LAYER_STACK;
     d.orientation = ui::ROTATION_0;
     d.orientedDisplaySpaceRect.makeInvalid();
     d.layerStackSpaceRect.makeInvalid();
@@ -4490,23 +4467,22 @@
 }
 
 sp<DisplayDevice> SurfaceFlinger::getDisplayWithInputByLayer(Layer* layer) const {
-    sp<DisplayDevice> display;
-    for (const auto& pair : mDisplays) {
-        const auto& displayDevice = pair.second;
-        if (!displayDevice->receivesInput() ||
-            !displayDevice->getCompositionDisplay()
-                     ->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+    const auto filter = layer->getOutputFilter();
+    sp<DisplayDevice> inputDisplay;
+
+    for (const auto& [_, display] : mDisplays) {
+        if (!display->receivesInput() || !display->getCompositionDisplay()->includesLayer(filter)) {
             continue;
         }
         // Don't return immediately so that we can log duplicates.
-        if (display) {
-            ALOGE("Multiple display devices claim to accept input for the same layerstack: %d",
-                  layer->getLayerStack());
+        if (inputDisplay) {
+            ALOGE("Multiple displays claim to accept input for the same layer stack: %u",
+                  filter.layerStack.id);
             continue;
         }
-        display = displayDevice;
+        inputDisplay = display;
     }
-    return display;
+    return inputDisplay;
 }
 
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4551,7 +4527,7 @@
 
         mVisibleRegionsDirty = true;
         mHasPoweredOff = true;
-        repaintEverything();
+        scheduleRefresh(FrameHint::kActive);
     } else if (mode == hal::PowerMode::OFF) {
         // Turn off the display
         if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
@@ -5143,7 +5119,7 @@
     colorizer.bold(result);
     result.append("h/w composer state:\n");
     colorizer.reset(result);
-    bool hwcDisabled = mDebugDisableHWC || mDebugRegion;
+    const bool hwcDisabled = mDebugDisableHWC || mDebugFlashDelay;
     StringAppendF(&result, "  h/w composer %s\n", hwcDisabled ? "disabled" : "enabled");
     getHwComposer().dump(result);
 
@@ -5352,9 +5328,8 @@
 
 status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                                     uint32_t flags) {
-    status_t credentialCheck = CheckTransactCodeCredentials(code);
-    if (credentialCheck != OK) {
-        return credentialCheck;
+    if (const status_t error = CheckTransactCodeCredentials(code); error != OK) {
+        return error;
     }
 
     status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
@@ -5371,47 +5346,43 @@
         }
         int n;
         switch (code) {
-            case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE
-            case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE
+            case 1000: // Unused.
+            case 1001:
+                return NAME_NOT_FOUND;
+            case 1002: // Toggle flashing on surface damage.
+                if (const int delay = data.readInt32(); delay > 0) {
+                    mDebugFlashDelay = delay;
+                } else {
+                    mDebugFlashDelay = mDebugFlashDelay ? 0 : 1;
+                }
+                scheduleRepaint();
                 return NO_ERROR;
-            case 1002:  // SHOW_UPDATES
-                n = data.readInt32();
-                mDebugRegion = n ? n : (mDebugRegion ? 0 : 1);
-                invalidateHwcGeometry();
-                repaintEverything();
+            case 1004: // Force refresh ahead of next VSYNC.
+                scheduleRefresh(FrameHint::kActive);
                 return NO_ERROR;
-            case 1004:{ // repaint everything
-                repaintEverything();
+            case 1005: { // Force commit ahead of next VSYNC.
+                Mutex::Autolock lock(mStateLock);
+                setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded |
+                                    eTraversalNeeded);
                 return NO_ERROR;
             }
-            case 1005:{ // force transaction
-                Mutex::Autolock _l(mStateLock);
-                setTransactionFlags(
-                        eTransactionNeeded|
-                        eDisplayTransactionNeeded|
-                        eTraversalNeeded);
-                return NO_ERROR;
-            }
-            case 1006:{ // send empty update
+            case 1006: // Force refresh immediately.
                 signalRefresh();
                 return NO_ERROR;
-            }
-            case 1008:  // toggle use of hw composer
-                n = data.readInt32();
-                mDebugDisableHWC = n != 0;
-                invalidateHwcGeometry();
-                repaintEverything();
+            case 1007: // Unused.
+                return NAME_NOT_FOUND;
+            case 1008: // Toggle forced GPU composition.
+                mDebugDisableHWC = data.readInt32() != 0;
+                scheduleRepaint();
                 return NO_ERROR;
-            case 1009:  // toggle use of transform hint
-                n = data.readInt32();
-                mDebugDisableTransformHint = n != 0;
-                invalidateHwcGeometry();
-                repaintEverything();
+            case 1009: // Toggle use of transform hint.
+                mDebugDisableTransformHint = data.readInt32() != 0;
+                scheduleRepaint();
                 return NO_ERROR;
-            case 1010:  // interrogate.
+            case 1010: // Interrogate.
                 reply->writeInt32(0);
                 reply->writeInt32(0);
-                reply->writeInt32(mDebugRegion);
+                reply->writeInt32(mDebugFlashDelay);
                 reply->writeInt32(0);
                 reply->writeInt32(mDebugDisableHWC);
                 return NO_ERROR;
@@ -5466,12 +5437,15 @@
                     mClientColorMatrix = mat4();
                 }
 
+                // TODO(b/193487656): Restore once HWASan bug is fixed.
+#if 0
                 // Check that supplied matrix's last row is {0,0,0,1} so we can avoid
                 // the division by w in the fragment shader
                 float4 lastRow(transpose(mClientColorMatrix)[3]);
                 if (any(greaterThan(abs(lastRow - float4{0, 0, 0, 1}), float4{1e-4f}))) {
                     ALOGE("The color transform's last row must be (0, 0, 0, 1)");
                 }
+#endif
 
                 updateColorMatrixLocked();
                 return NO_ERROR;
@@ -5525,8 +5499,7 @@
                 if (data.readInt32(&colorMode) == NO_ERROR) {
                     mForceColorMode = static_cast<ColorMode>(colorMode);
                 }
-                invalidateHwcGeometry();
-                repaintEverything();
+                scheduleRepaint();
                 return NO_ERROR;
             }
             // Deprecate, use 1030 to check whether the device is color managed.
@@ -5657,37 +5630,24 @@
             }
             case 1035: {
                 const int modeId = data.readInt32();
-                mDebugDisplayModeSetByBackdoor = false;
 
-                const auto displayId = [&]() -> std::optional<PhysicalDisplayId> {
-                    uint64_t inputDisplayId = 0;
-                    if (data.readUint64(&inputDisplayId) == NO_ERROR) {
-                        const auto token = getPhysicalDisplayToken(
-                                static_cast<PhysicalDisplayId>(inputDisplayId));
-                        if (!token) {
-                            ALOGE("No display with id: %" PRIu64, inputDisplayId);
-                            return std::nullopt;
-                        }
-
-                        return std::make_optional<PhysicalDisplayId>(inputDisplayId);
+                const auto display = [&]() -> sp<IBinder> {
+                    uint64_t value;
+                    if (data.readUint64(&value) != NO_ERROR) {
+                        return getDefaultDisplayDevice()->getDisplayToken().promote();
                     }
 
-                    return getDefaultDisplayDevice()->getPhysicalId();
+                    if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(value)) {
+                        return getPhysicalDisplayToken(*id);
+                    }
+
+                    ALOGE("Invalid physical display ID");
+                    return nullptr;
                 }();
 
-                if (!displayId) {
-                    ALOGE("No display found");
-                    return NO_ERROR;
-                }
-
-                status_t result = setActiveMode(getPhysicalDisplayToken(*displayId), modeId);
-                if (result != NO_ERROR) {
-                    return result;
-                }
-
-                mDebugDisplayModeSetByBackdoor = true;
-
-                return NO_ERROR;
+                const status_t result = setActiveMode(display, modeId);
+                mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
+                return result;
             }
             case 1036: {
                 if (data.readInt32() > 0) {
@@ -5704,12 +5664,10 @@
             // Inject a hotplug connected event for the primary display. This will deallocate and
             // reallocate the display state including framebuffers.
             case 1037: {
-                std::optional<hal::HWDisplayId> hwcId;
-                {
-                    Mutex::Autolock lock(mStateLock);
-                    hwcId = getHwComposer().getInternalHwcDisplayId();
-                }
-                onComposerHalHotplug(*hwcId, hal::Connection::CONNECTED);
+                const hal::HWDisplayId hwcId =
+                        (Mutex::Autolock(mStateLock), getHwComposer().getPrimaryHwcDisplayId());
+
+                onComposerHalHotplug(hwcId, hal::Connection::CONNECTED);
                 return NO_ERROR;
             }
             // Modify the max number of display frames stored within FrameTimeline
@@ -5750,14 +5708,11 @@
                             std::optional<PhysicalDisplayId> inputId = std::nullopt;
                             if (uint64_t inputDisplayId;
                                 data.readUint64(&inputDisplayId) == NO_ERROR) {
-                                const auto token = getPhysicalDisplayToken(
-                                        static_cast<PhysicalDisplayId>(inputDisplayId));
-                                if (!token) {
+                                inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId);
+                                if (!inputId || getPhysicalDisplayToken(*inputId)) {
                                     ALOGE("No display with id: %" PRIu64, inputDisplayId);
                                     return NAME_NOT_FOUND;
                                 }
-
-                                inputId = std::make_optional<PhysicalDisplayId>(inputDisplayId);
                             }
                             {
                                 Mutex::Autolock lock(mStateLock);
@@ -5774,8 +5729,7 @@
                 if (error != OK) {
                     return error;
                 }
-                invalidateHwcGeometry();
-                repaintEverything();
+                scheduleRepaint();
                 return NO_ERROR;
             }
         }
@@ -5783,17 +5737,6 @@
     return err;
 }
 
-void SurfaceFlinger::repaintEverything() {
-    mRepaintEverything = true;
-    signalTransaction();
-}
-
-void SurfaceFlinger::repaintEverythingForHWC() {
-    mRepaintEverything = true;
-    mPowerAdvisor.notifyDisplayUpdateImminent();
-    mEventQueue->invalidate();
-}
-
 void SurfaceFlinger::kernelTimerChanged(bool expired) {
     static bool updateOverlay =
             property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
@@ -6015,7 +5958,7 @@
                                captureListener);
 }
 
-status_t SurfaceFlinger::captureDisplay(uint64_t displayIdOrLayerStack,
+status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
                                         const sp<IScreenCaptureListener>& captureListener) {
     ui::LayerStack layerStack;
     wp<const DisplayDevice> displayWeak;
@@ -6023,21 +5966,14 @@
     ui::Dataspace dataspace;
     {
         Mutex::Autolock lock(mStateLock);
-        auto display = getDisplayDeviceLocked(PhysicalDisplayId{displayIdOrLayerStack});
 
-        // Fall back to first display whose layer stack matches.
-        if (!display) {
-            const auto layerStack = static_cast<ui::LayerStack>(displayIdOrLayerStack);
-            display = findDisplay(WithLayerStack(layerStack));
-        }
-
+        const auto display = getDisplayDeviceLocked(displayId);
         if (!display) {
             return NAME_NOT_FOUND;
         }
 
-        layerStack = display->getLayerStack();
         displayWeak = display;
-
+        layerStack = display->getLayerStack();
         size = display->getLayerStackSpaceRect().getSize();
 
         dataspace =
@@ -6122,7 +6058,11 @@
             }
         }
 
-        const auto display = findDisplay(WithLayerStack(parent->getLayerStack()));
+        const auto display =
+                findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
+                    return display.getLayerStack() == layerStack;
+                });
+
         if (!display) {
             return NAME_NOT_FOUND;
         }
@@ -6337,7 +6277,6 @@
 
     const auto display = renderArea.getDisplayDevice();
     std::vector<Layer*> renderedLayers;
-    Region clearRegion = Region::INVALID_REGION;
     bool disableBlurs = false;
     traverseLayers([&](Layer* layer) {
         disableBlurs |= layer->getDrawingState().sidebandStream != nullptr;
@@ -6349,7 +6288,6 @@
                         renderArea.needsFiltering(),
                 renderArea.isSecure(),
                 useProtected,
-                clearRegion,
                 layerStackSpaceRect,
                 clientCompositionDisplay.outputDataspace,
                 true,  /* realContentIsVisible */
@@ -6387,7 +6325,6 @@
                    clientCompositionLayerPointers.begin(),
                    std::pointer_traits<renderengine::LayerSettings*>::pointer_to);
 
-    clientCompositionDisplay.clearRegion = clearRegion;
     // Use an empty fence for the buffer fence, since we just created the buffer so
     // there is no need for synchronization with the GPU.
     base::unique_fd bufferFence;
@@ -6436,12 +6373,12 @@
     // We loop through the first level of layers without traversing,
     // as we need to determine which layers belong to the requested display.
     for (const auto& layer : mDrawingState.layersSortedByZ) {
-        if (!layer->belongsToDisplay(layerStack)) {
+        if (layer->getLayerStack() != layerStack) {
             continue;
         }
         // relative layers are traversed in Layer::traverseInZOrder
         layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
-            if (layer->getPrimaryDisplayOnly()) {
+            if (layer->isInternalDisplayOverlay()) {
                 return;
             }
             if (!layer->isVisible()) {
@@ -6800,33 +6737,29 @@
 }
 
 status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
-    const auto maxSupportedRefreshRate = [&] {
-        const auto display = getDefaultDisplayDevice();
-        if (display) {
-            return display->refreshRateConfigs().getSupportedRefreshRateRange().max;
+    Fps maxRefreshRate(60.f);
+
+    if (!getHwComposer().isHeadless()) {
+        if (const auto display = getDefaultDisplayDevice()) {
+            maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max;
         }
-        ALOGW("%s: default display is null", __func__);
-        return Fps(60);
-    }();
-    *buffers = getMaxAcquiredBufferCountForRefreshRate(maxSupportedRefreshRate);
+    }
+
+    *buffers = getMaxAcquiredBufferCountForRefreshRate(maxRefreshRate);
     return NO_ERROR;
 }
 
-int SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const {
-    const auto refreshRate = [&] {
-        const auto frameRateOverride = mScheduler->getFrameRateOverride(uid);
-        if (frameRateOverride.has_value()) {
-            return frameRateOverride.value();
-        }
+uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const {
+    Fps refreshRate(60.f);
 
-        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-        if (display) {
-            return display->refreshRateConfigs().getCurrentRefreshRate().getFps();
+    if (const auto frameRateOverride = mScheduler->getFrameRateOverride(uid)) {
+        refreshRate = *frameRateOverride;
+    } else if (!getHwComposer().isHeadless()) {
+        if (const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked())) {
+            refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
         }
+    }
 
-        ALOGW("%s: default display is null", __func__);
-        return Fps(60);
-    }();
     return getMaxAcquiredBufferCountForRefreshRate(refreshRate);
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ebc3e4a..33bc17b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -285,8 +285,12 @@
     template <typename F, typename T = std::invoke_result_t<F>>
     [[nodiscard]] std::future<T> schedule(F&&);
 
-    // force full composition on all displays
-    void repaintEverything();
+    // Schedule commit of transactions on the main thread ahead of the next VSYNC.
+    void scheduleInvalidate(FrameHint);
+    // As above, but also force refresh regardless if transactions were committed.
+    void scheduleRefresh(FrameHint) override;
+    // As above, but also force dirty geometry to repaint.
+    void scheduleRepaint();
 
     surfaceflinger::Factory& getFactory() { return mFactory; }
 
@@ -348,7 +352,6 @@
             uint32_t permissions,
             std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
             REQUIRES(mStateLock);
-    virtual void commitTransactionLocked();
 
     // Used internally by computeLayerBounds() to gets the clip rectangle to use for the
     // root layers on a particular display in layer-coordinate space. The
@@ -635,12 +638,10 @@
     sp<IDisplayEventConnection> createDisplayEventConnection(
             ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
             ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
-    status_t captureDisplay(const DisplayCaptureArgs& args,
-                            const sp<IScreenCaptureListener>& captureListener) override;
-    status_t captureDisplay(uint64_t displayOrLayerStack,
-                            const sp<IScreenCaptureListener>& captureListener) override;
-    status_t captureLayers(const LayerCaptureArgs& args,
-                           const sp<IScreenCaptureListener>& captureListener) override;
+
+    status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override;
+    status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override;
+    status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override;
 
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
     status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
@@ -753,8 +754,6 @@
     void setVsyncEnabled(bool) override;
     // Initiates a refresh rate change to be applied on invalidate.
     void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override;
-    // Forces full composition on all displays without resetting the scheduler idle timer.
-    void repaintEverythingForHWC() override;
     // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
     void kernelTimerChanged(bool expired) override;
     // Called when the frame rate override list changed to trigger an event.
@@ -773,8 +772,6 @@
      * Message handling
      */
     // Can only be called from the main thread or with mStateLock held
-    void signalTransaction();
-    // Can only be called from the main thread or with mStateLock held
     void signalLayerUpdate();
     void signalRefresh();
 
@@ -806,8 +803,12 @@
     // incoming transactions
     void onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime);
 
-    // Returns whether the transaction actually modified any state
-    bool handleMessageTransaction();
+    // Returns whether transactions were committed.
+    bool flushAndCommitTransactions() EXCLUDES(mStateLock);
+
+    void commitTransactions() EXCLUDES(mStateLock);
+    void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
+    void doCommitTransactions() REQUIRES(mStateLock);
 
     // Handle the REFRESH message queue event, sending the current frame down to RenderEngine and
     // the Composer HAL for presentation
@@ -816,9 +817,6 @@
     // Returns whether a new buffer has been latched (see handlePageFlip())
     bool handleMessageInvalidate();
 
-    void handleTransaction(uint32_t transactionFlags);
-    void handleTransactionLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
-
     void updateInputFlinger();
     void notifyWindowInfos();
     void commitInputWindowCommands() REQUIRES(mStateLock);
@@ -850,18 +848,23 @@
     void flushTransactionQueues();
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
-    uint32_t getTransactionFlags(uint32_t flags);
-    uint32_t peekTransactionFlags();
-    // Can only be called from the main thread or with mStateLock held
-    uint32_t setTransactionFlags(uint32_t flags);
+
+    uint32_t getTransactionFlags() const;
+
+    // Sets the masked bits, and returns the old flags.
+    uint32_t setTransactionFlags(uint32_t mask);
+
+    // Clears and returns the masked bits.
+    uint32_t clearTransactionFlags(uint32_t mask);
+
     // Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases
     // where there are still pending transactions but we know they won't be ready until a frame
     // arrives from a different layer. So we need to ensure we performTransaction from invalidate
     // but there is no need to try and wake up immediately to do it. Rather we rely on
     // onFrameAvailable or another layer update to wake us up.
     void setTraversalNeeded();
-    uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule, const sp<IBinder>& = {});
-    void commitTransaction() REQUIRES(mStateLock);
+    uint32_t setTransactionFlags(uint32_t mask, TransactionSchedule,
+                                 const sp<IBinder>& applyToken = {});
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
@@ -944,7 +947,7 @@
         return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize;
     }
 
-    int getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const;
+    uint32_t getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const;
 
     /*
      * Display and layer stack management
@@ -1035,8 +1038,6 @@
     /*
      * Compositing
      */
-    void invalidateHwcGeometry();
-
     void postComposition();
     void getCompositorTiming(CompositorTiming* compositorTiming);
     void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
@@ -1120,15 +1121,19 @@
         return {};
     }
 
-    // TODO(b/74619554): Remove special cases for primary display.
+    // TODO(b/182939859): SF conflates the primary (a.k.a. default) display with the first display
+    // connected at boot, which is typically internal. (Theoretically, it must be internal because
+    // SF does not support disconnecting it, though in practice HWC may circumvent this limitation.)
+    //
+    // SF inherits getInternalDisplayToken and getInternalDisplayId from ISurfaceComposer, so these
+    // locked counterparts are named consistently. Once SF supports headless mode and can designate
+    // any display as primary, the "internal" misnomer will be phased out.
     sp<IBinder> getInternalDisplayTokenLocked() const REQUIRES(mStateLock) {
-        const auto displayId = getInternalDisplayIdLocked();
-        return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
+        return getPhysicalDisplayTokenLocked(getInternalDisplayIdLocked());
     }
 
-    std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
-        const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
-        return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
+    PhysicalDisplayId getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
+        return getHwComposer().getPrimaryDisplayId();
     }
 
     // Toggles use of HAL/GPU virtual displays.
@@ -1207,8 +1212,7 @@
     /*
      * Misc
      */
-    std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId displayId)
-            REQUIRES(mStateLock);
+    std::vector<ui::ColorMode> getDisplayColorModes(const DisplayDevice&) REQUIRES(mStateLock);
 
     static int calculateMaxAcquiredBufferCount(Fps refreshRate,
                                                std::chrono::nanoseconds presentLatency);
@@ -1250,7 +1254,8 @@
     bool mLayersRemoved = false;
     bool mLayersAdded = false;
 
-    std::atomic<bool> mRepaintEverything = false;
+    std::atomic_bool mForceRefresh = false;
+    std::atomic_bool mGeometryDirty = false;
 
     // constant members (no synchronization needed for access)
     const nsecs_t mBootTime = systemTime();
@@ -1277,7 +1282,6 @@
     bool mSomeDataspaceChanged = false;
     bool mForceTransactionDisplayChange = false;
 
-    bool mGeometryInvalid = false;
     bool mAnimCompositionPending = false;
 
     // Tracks layers that have pending frames which are candidates for being
@@ -1312,13 +1316,13 @@
         std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
     } mVirtualDisplayIdGenerators;
 
-    // don't use a lock for these, we don't care
-    int mDebugRegion = 0;
-    bool mDebugDisableHWC = false;
-    bool mDebugDisableTransformHint = false;
+    std::atomic_uint mDebugFlashDelay = 0;
+    std::atomic_bool mDebugDisableHWC = false;
+    std::atomic_bool mDebugDisableTransformHint = false;
+    std::atomic<nsecs_t> mDebugInTransaction = 0;
+    std::atomic_bool mForceFullDamage = false;
+
     bool mLayerCachingEnabled = false;
-    volatile nsecs_t mDebugInTransaction = 0;
-    bool mForceFullDamage = false;
     bool mPropagateBackpressureClientComposition = false;
     sp<SurfaceInterceptor> mInterceptor;
 
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 9be3abe..0782fef 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -323,11 +323,10 @@
 }
 
 void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
-        uint32_t layerStack)
-{
+                                             ui::LayerStack layerStack) {
     SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
     LayerStackChange* layerStackChange(change->mutable_layer_stack());
-    layerStackChange->set_layer_stack(layerStack);
+    layerStackChange->set_layer_stack(layerStack.id);
 }
 
 void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
@@ -568,12 +567,11 @@
     }
 }
 
-void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction,
-        int32_t sequenceId, uint32_t layerStack)
-{
+void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
+                                                    ui::LayerStack layerStack) {
     DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
     LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
-    layerStackChange->set_layer_stack(layerStack);
+    layerStackChange->set_layer_stack(layerStack.id);
 }
 
 void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId,
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 9a3ce2c..970c3e5 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -48,7 +48,7 @@
 using Increment = surfaceflinger::Increment;
 using DisplayChange = surfaceflinger::DisplayChange;
 
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
 
 class SurfaceInterceptor : public IBinder::DeathRecipient {
 public:
@@ -160,7 +160,7 @@
     void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
             const Region& transRegion);
     void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask);
-    void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
+    void addLayerStackLocked(Transaction* transaction, int32_t layerId, ui::LayerStack);
     void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
     void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
     void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
@@ -183,8 +183,7 @@
     DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
     void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
             const sp<const IGraphicBufferProducer>& surface);
-    void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
-            uint32_t layerStack);
+    void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, ui::LayerStack);
     void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags);
     void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w,
             uint32_t h);
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 15a503d..42a16c6 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -77,7 +77,7 @@
 private:
     class Runner;
     static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
-    static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.pb";
+    static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
 
     SurfaceFlinger& mFlinger;
     mutable std::mutex mTraceLock;
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index fa3f0e7..d33bc10 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -93,7 +93,7 @@
         ASSERT_TRUE(mBGSurfaceControl->isValid());
 
         Transaction t;
-        t.setDisplayLayerStack(mDisplay, 0);
+        t.setDisplayLayerStack(mDisplay, ui::DEFAULT_LAYER_STACK);
         ASSERT_EQ(NO_ERROR,
                   t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply());
     }
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index af00ec7..1361ded 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -33,7 +33,7 @@
 
         mParentLayer = createColorLayer("Parent layer", Color::RED);
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
             t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
             t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
         });
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 9fa3d4c..8d7e175 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -159,7 +159,7 @@
                                                  {0, 0, static_cast<int32_t>(width),
                                                   static_cast<int32_t>(height)},
                                                  Color::RED);
-        transaction->setLayerStack(mSurfaceControl, 0)
+        transaction->setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
                 .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
                 .setBuffer(mSurfaceControl, gb)
                 .setAcquireFence(mSurfaceControl, fence)
@@ -232,7 +232,7 @@
         mDisplayHeight = mode.resolution.getHeight();
 
         Transaction setupTransaction;
-        setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
+        setupTransaction.setDisplayLayerStack(mPrimaryDisplay, ui::DEFAULT_LAYER_STACK);
         setupTransaction.apply();
     }
 
@@ -310,7 +310,7 @@
                                              Color::RED);
 
     Transaction transaction;
-    transaction.setLayerStack(sc, 0)
+    transaction.setLayerStack(sc, ui::DEFAULT_LAYER_STACK)
             .setLayer(sc, std::numeric_limits<int32_t>::max() - 1)
             .setBuffer(sc, gb)
             .setAcquireFence(sc, fence)
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 0bc8fe7..6bd7920 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -266,7 +266,7 @@
     sp<IBinder> mDisplay;
     uint32_t mDisplayWidth;
     uint32_t mDisplayHeight;
-    uint32_t mDisplayLayerStack;
+    ui::LayerStack mDisplayLayerStack = ui::DEFAULT_LAYER_STACK;
     Rect mDisplayRect = Rect::INVALID_RECT;
 
     // leave room for ~256 layers
@@ -294,8 +294,6 @@
         // vsyncs.
         mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3;
 
-        mDisplayLayerStack = 0;
-
         mBlackBgSurface =
                 createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
                               PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 43d957c..4076253 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -643,7 +643,8 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
 
-    Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
+    const auto layerStack = ui::LayerStack::fromValue(mDisplayLayerStack.id + 1);
+    Transaction().setLayerStack(layer, layerStack).apply();
     {
         SCOPED_TRACE("non-existing layer stack");
         getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index ee4d367..e1a7ecc 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -63,7 +63,7 @@
         TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
 
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
             t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
 
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index d027865..3ec6da9 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -36,7 +36,7 @@
         mParentLayer = createColorLayer("Parent layer", Color::RED);
         mChildLayer = createColorLayer("Child layer", Color::GREEN, mParentLayer.get());
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
             t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
             t.setCrop(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
             t.setPosition(mChildLayer, 50, 50);
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 08de01c..1ed6c65 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -52,7 +52,7 @@
         mColorLayer = 0;
     }
 
-    void createDisplay(const ui::Size& layerStackSize, uint32_t layerStack) {
+    void createDisplay(const ui::Size& layerStackSize, ui::LayerStack layerStack) {
         mVirtualDisplay =
                 SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
         asTransaction([&](Transaction& t) {
@@ -63,7 +63,7 @@
         });
     }
 
-    void createColorLayer(uint32_t layerStack) {
+    void createColorLayer(ui::LayerStack layerStack) {
         mColorLayer =
                 createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
                               PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
@@ -90,8 +90,9 @@
 };
 
 TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
-    createDisplay(mMainDisplayState.layerStackSpaceRect, 1 /* layerStack */);
-    createColorLayer(1 /* layerStack */);
+    constexpr ui::LayerStack kLayerStack{1u};
+    createDisplay(mMainDisplayState.layerStackSpaceRect, kLayerStack);
+    createColorLayer(kLayerStack);
 
     asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
 
@@ -113,8 +114,8 @@
 
     // Assumption here is that the new mirrored display has the same layer stack rect as the
     // primary display that it is mirroring.
-    createDisplay(mMainDisplayState.layerStackSpaceRect, 0 /* layerStack */);
-    createColorLayer(0 /* layerStack */);
+    createDisplay(mMainDisplayState.layerStackSpaceRect, ui::DEFAULT_LAYER_STACK);
+    createColorLayer(ui::DEFAULT_LAYER_STACK);
 
     asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
 
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index fde6e6e..50a4092 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -43,7 +43,7 @@
         mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
 
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
             t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
             t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
         });
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index ab2064e..95301b3 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -53,7 +53,7 @@
         TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
 
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
             t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
 
@@ -563,7 +563,7 @@
     Transaction()
             .show(redLayer)
             .show(secureLayer)
-            .setLayerStack(redLayer, 0)
+            .setLayerStack(redLayer, ui::DEFAULT_LAYER_STACK)
             .setLayer(redLayer, INT32_MAX)
             .apply();
 
@@ -655,7 +655,7 @@
     Transaction()
             .show(layer)
             .hide(mFGSurfaceControl)
-            .setLayerStack(layer, 0)
+            .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
             .setCrop(layer, bounds)
@@ -702,7 +702,7 @@
             .show(layer)
             .show(childLayer)
             .hide(mFGSurfaceControl)
-            .setLayerStack(layer, 0)
+            .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
             .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index d5890ff..ee16f40 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -61,7 +61,7 @@
 constexpr auto LAYER_NAME = "Layer Create and Delete Test";
 constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0";
 
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
 
 // Fill an RGBA_8888 formatted surface with a single color.
 static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
@@ -283,7 +283,7 @@
     ASSERT_TRUE(mFGSurfaceControl->isValid());
 
     Transaction t;
-    t.setDisplayLayerStack(display, 0);
+    t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
     ASSERT_EQ(NO_ERROR,
               t.setLayer(mBGSurfaceControl, INT_MAX - 3)
                       .show(mBGSurfaceControl)
@@ -380,7 +380,7 @@
 }
 
 void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) {
-    t.setLayerStack(mBGSurfaceControl, STACK_UPDATE);
+    t.setLayerStack(mBGSurfaceControl, ui::LayerStack::fromValue(STACK_UPDATE));
 }
 
 void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) {
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 89f6086..60cffb1 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -65,7 +65,7 @@
 
                 SurfaceComposerClient::Transaction t;
                 t.setDisplaySurface(vDisplay, producer);
-                t.setDisplayLayerStack(vDisplay, 0);
+                t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK);
                 t.setDisplayProjection(vDisplay, displayState.orientation,
                                        Rect(displayState.layerStackSpaceRect), Rect(resolution));
                 t.apply();
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index 89228d5..de116f2 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -84,7 +84,7 @@
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
 
     Transaction()
-            .setLayerStack(surfaceControl, 0)
+            .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK)
             .show(surfaceControl)
             .setLayer(surfaceControl, INT32_MAX - 1)
             .setInputWindowInfo(surfaceControl, windowInfo)
@@ -112,7 +112,7 @@
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
 
     Transaction()
-            .setLayerStack(surfaceControl, 0)
+            .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK)
             .show(surfaceControl)
             .setLayer(surfaceControl, INT32_MAX - 1)
             .setInputWindowInfo(surfaceControl, windowInfo)
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 162711d..b3b4ec1 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -279,7 +279,7 @@
     }
 
     bool waitForHotplugEvent(Display displayId, bool connected) {
-        return waitForHotplugEvent(PhysicalDisplayId(displayId), connected);
+        return waitForHotplugEvent(physicalIdFromHwcDisplayId(displayId), connected);
     }
 
     bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
@@ -305,7 +305,7 @@
     }
 
     bool waitForModeChangedEvent(Display display, int32_t modeId) {
-        PhysicalDisplayId displayId(display);
+        PhysicalDisplayId displayId = physicalIdFromHwcDisplayId(display);
         int waitCount = 20;
         while (waitCount--) {
             while (!mReceivedDisplayEvents.empty()) {
@@ -363,7 +363,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -426,7 +426,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -479,7 +479,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -534,7 +534,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -586,7 +586,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -651,7 +651,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -703,7 +703,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -750,7 +750,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -797,7 +797,7 @@
 
             {
                 TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, 0);
+                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
                 ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
@@ -1195,7 +1195,7 @@
         fillSurfaceRGBA8(mFGSurfaceControl, RED);
 
         Transaction t;
-        t.setDisplayLayerStack(display, 0);
+        t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
 
         t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
         t.show(mBGSurfaceControl);
@@ -1342,7 +1342,7 @@
         ALOGD("TransactionTest::SetLayerStack");
         {
             TransactionScope ts(*sFakeComposer);
-            ts.setLayerStack(mFGSurfaceControl, 1);
+            ts.setLayerStack(mFGSurfaceControl, ui::LayerStack{1});
         }
 
         // Foreground layer should have disappeared.
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 9e704c3..200ecbd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -23,7 +23,10 @@
 
 cc_test {
     name: "libsurfaceflinger_unittest",
-    defaults: ["surfaceflinger_defaults"],
+    defaults: [
+        "skia_renderengine_deps",
+        "surfaceflinger_defaults",
+    ],
     test_suites: ["device-tests"],
     sanitize: {
         // Using the address sanitizer not only helps uncover issues in the code
@@ -67,10 +70,10 @@
         "MessageQueueTest.cpp",
         "SurfaceFlinger_CreateDisplayTest.cpp",
         "SurfaceFlinger_DestroyDisplayTest.cpp",
+        "SurfaceFlinger_DisplayTransactionCommitTest.cpp",
         "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
-        "SurfaceFlinger_HandleTransactionLockedTest.cpp",
-        "SurfaceFlinger_NotifyPowerBoostTest.cpp",
         "SurfaceFlinger_HotplugTest.cpp",
+        "SurfaceFlinger_NotifyPowerBoostTest.cpp",
         "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
         "SurfaceFlinger_SetDisplayStateTest.cpp",
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 6a7ec9b..6f85498 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -14,11 +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"
-#pragma clang diagnostic ignored "-Wextra"
-
 #undef LOG_TAG
 #define LOG_TAG "CachingTest"
 
@@ -42,7 +37,7 @@
     sp<IBinder> binder = new BBinder();
     // test getting invalid client_cache_id
     client_cache_t id;
-    uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+    int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
     EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
 }
 
@@ -51,7 +46,7 @@
     client_cache_t id;
     id.token = binder;
     id.id = 0;
-    uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+    int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
     EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
 
     client_cache_t idB;
@@ -72,31 +67,28 @@
     std::vector<client_cache_t> ids;
     uint32_t cacheId = 0;
     // fill up cache
-    for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
         client_cache_t id;
         id.token = binder;
         id.id = cacheId;
         ids.push_back(id);
 
-        uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+        int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
         EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
         cacheId++;
     }
-    for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(ids[i]);
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]);
         EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
     }
 
-    for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
         client_cache_t id;
         id.token = binder;
         id.id = cacheId;
-        uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+        int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
         EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
         cacheId++;
     }
 }
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 52a36a2..93a4ce2 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -77,12 +77,12 @@
 constexpr hal::HWLayerId HWC_LAYER = 5000;
 constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
 
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(42);
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
 constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
 
 constexpr int DEFAULT_TEXTURE_ID = 6000;
-constexpr int DEFAULT_LAYER_STACK = 7000;
+constexpr ui::LayerStack LAYER_STACK{7000u};
 
 constexpr int DEFAULT_DISPLAY_MAX_LUMINANCE = 500;
 
@@ -157,7 +157,7 @@
         // pain)
         // mFlinger.mutableVisibleRegionsDirty() = true;
 
-        mFlinger.mutableGeometryInvalid() = true;
+        mFlinger.mutableGeometryDirty() = true;
     }
 
     template <typename Case>
@@ -287,10 +287,8 @@
 
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
                                      .setId(DEFAULT_DISPLAY_ID)
-                                     .setConnectionType(ui::DisplayConnectionType::Internal)
                                      .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
                                      .setIsSecure(Derived::IS_SECURE)
-                                     .setLayerStackId(DEFAULT_LAYER_STACK)
                                      .setPowerAdvisor(&test->mPowerAdvisor)
                                      .setName(std::string("Injected display for ") +
                                               test_info->test_case_name() + "." + test_info->name())
@@ -309,7 +307,7 @@
                                  .setPowerMode(Derived::INIT_POWER_MODE)
                                  .inject();
         Mock::VerifyAndClear(test->mNativeWindow);
-        test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK);
+        test->mDisplay->setLayerStack(LAYER_STACK);
     }
 
     template <typename Case>
@@ -834,7 +832,7 @@
     template <typename L>
     static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
-        layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
+        layerDrawingState.layerStack = LAYER_STACK;
         layerDrawingState.width = 100;
         layerDrawingState.height = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index cc24323..5a21e7b 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -122,7 +122,7 @@
 
 sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
         std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
-    constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777);
+    constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
     constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
     constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
     constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
@@ -139,20 +139,18 @@
     EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
     EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
 
-    constexpr auto kConnectionType = ui::DisplayConnectionType::Internal;
-    constexpr bool kIsPrimary = true;
-
     auto compositionDisplay =
             compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
                                                    compositionengine::DisplayCreationArgsBuilder()
                                                            .setId(DEFAULT_DISPLAY_ID)
-                                                           .setConnectionType(kConnectionType)
                                                            .setPixels({DEFAULT_DISPLAY_WIDTH,
                                                                        DEFAULT_DISPLAY_HEIGHT})
                                                            .setPowerAdvisor(&mPowerAdvisor)
                                                            .build());
 
-    auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, kConnectionType,
+    constexpr bool kIsPrimary = true;
+    auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+                                              ui::DisplayConnectionType::Internal,
                                               DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
 
     injector.setNativeWindow(mNativeWindow);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index de058a4..0f1cc67 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -267,11 +267,6 @@
                 .setPixels({WIDTH, HEIGHT})
                 .setPowerAdvisor(&test->mPowerAdvisor);
 
-        const auto connectionType = CONNECTION_TYPE::value;
-        if (connectionType) {
-            ceDisplayArgs.setConnectionType(*connectionType);
-        }
-
         auto compositionDisplay =
                 compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
                                                        ceDisplayArgs.build());
@@ -279,7 +274,7 @@
         auto injector =
                 TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
                                                                   compositionDisplay,
-                                                                  connectionType,
+                                                                  CONNECTION_TYPE::value,
                                                                   HWC_DISPLAY_ID_OPT::value,
                                                                   static_cast<bool>(PRIMARY));
 
@@ -388,7 +383,6 @@
 
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
                                      .setId(DisplayVariant::DISPLAY_ID::get())
-                                     .setConnectionType(PhysicalDisplay::CONNECTION_TYPE)
                                      .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
                                      .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
                                      .setPowerAdvisor(&test->mPowerAdvisor)
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 4ff7592..28d0222 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -41,10 +41,13 @@
 
 namespace {
 
-constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111);
-constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222);
-constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL);
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(111u);
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(222u);
+constexpr PhysicalDisplayId DISPLAY_ID_64BIT =
+        PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu);
+
 constexpr std::chrono::duration VSYNC_PERIOD(16ms);
+
 class MockVSyncSource : public VSyncSource {
 public:
     const char* getName() const override { return "test"; }
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 02ec7fc..bb21ad6 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -118,13 +118,15 @@
     std::shared_ptr<RefreshRateConfigs> mConfigs = std::make_shared<
             RefreshRateConfigs>(DisplayModes{DisplayMode::Builder(0)
                                                      .setId(DisplayModeId(0))
-                                                     .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                                     .setPhysicalDisplayId(
+                                                             PhysicalDisplayId::fromPort(0))
                                                      .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
                                                      .setGroup(0)
                                                      .build(),
                                              DisplayMode::Builder(1)
                                                      .setId(DisplayModeId(1))
-                                                     .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                                     .setPhysicalDisplayId(
+                                                             PhysicalDisplayId::fromPort(0))
                                                      .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
                                                      .setGroup(0)
                                                      .build()},
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index 6916764..597e5e7 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -72,7 +72,8 @@
     mIdleTimer->stop();
 }
 
-TEST_F(OneShotTimerTest, resetTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_resetTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
@@ -94,7 +95,8 @@
     EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
-TEST_F(OneShotTimerTest, resetBackToBackTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_resetBackToBackTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
@@ -144,7 +146,8 @@
     EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
-TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_idleTimerIdlesTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
@@ -169,7 +172,8 @@
     EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
-TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_timeoutCallbackExecutionTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 6870fd4..a6bfde7 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -98,9 +98,15 @@
     static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4);
     static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5);
     static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6);
+    static inline const DisplayModeId HWC_CONFIG_ID_24 = DisplayModeId(7);
+    static inline const DisplayModeId HWC_CONFIG_ID_24_FRAC = DisplayModeId(8);
+    static inline const DisplayModeId HWC_CONFIG_ID_30_FRAC = DisplayModeId(9);
+    static inline const DisplayModeId HWC_CONFIG_ID_60_FRAC = DisplayModeId(10);
 
     // Test configs
     DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
+    DisplayModePtr mConfig60Frac =
+            createDisplayMode(HWC_CONFIG_ID_60_FRAC, 0, Fps(59.94f).getPeriodNsecs());
     DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
     DisplayModePtr mConfig90DifferentGroup =
             createDisplayMode(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs());
@@ -116,9 +122,15 @@
     DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
     DisplayModePtr mConfig30DifferentGroup =
             createDisplayMode(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs());
+    DisplayModePtr mConfig30Frac =
+            createDisplayMode(HWC_CONFIG_ID_30_FRAC, 0, Fps(29.97f).getPeriodNsecs());
+    DisplayModePtr mConfig25 = createDisplayMode(HWC_CONFIG_ID_25, 0, Fps(25.0f).getPeriodNsecs());
     DisplayModePtr mConfig25DifferentGroup =
             createDisplayMode(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs());
     DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
+    DisplayModePtr mConfig24 = createDisplayMode(HWC_CONFIG_ID_24, 0, Fps(24.0f).getPeriodNsecs());
+    DisplayModePtr mConfig24Frac =
+            createDisplayMode(HWC_CONFIG_ID_24_FRAC, 0, Fps(23.976f).getPeriodNsecs());
 
     // Test device configurations
     // The positions of the configs in the arrays below MUST match their IDs. For example,
@@ -145,6 +157,11 @@
                                        mConfig50};
     DisplayModes m60_120Device = {mConfig60, mConfig120};
 
+    // This is a typical TV configuration.
+    DisplayModes m24_25_30_50_60WithFracDevice = {mConfig24, mConfig24Frac, mConfig25,
+                                                  mConfig30, mConfig30Frac, mConfig50,
+                                                  mConfig60, mConfig60Frac};
+
     // Expected RefreshRate objects
     RefreshRate mExpected60Config = {mConfig60, RefreshRate::ConstructorTag(0)};
     RefreshRate mExpectedAlmost60Config = {createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665),
@@ -181,7 +198,7 @@
                                                          int64_t vsyncPeriod, ui::Size resolution) {
     return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
             .setId(modeId)
-            .setPhysicalDisplayId(PhysicalDisplayId(0))
+            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
             .setVsyncPeriod(int32_t(vsyncPeriod))
             .setGroup(group)
             .setHeight(resolution.height)
@@ -1230,7 +1247,109 @@
         const auto& refreshRate =
                 refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
         EXPECT_TRUE(refreshRate.getFps().equalsWithMargin(Fps(test.second)))
-                << "Expecting " << test.first << "fps => " << test.second << "Hz";
+                << "Expecting " << test.first << "fps => " << test.second << "Hz"
+                << " but it was " << refreshRate.getFps();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest,
+       getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    // Test that 23.976 will choose 24 if 23.976 is not supported
+    {
+        android::DisplayModes modes = {mConfig24,     mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+        lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+        lr.desiredRefreshRate = Fps(23.976f);
+        lr.name = "ExplicitExactOrMultiple 23.976 fps";
+        EXPECT_EQ(HWC_CONFIG_ID_24,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
+    }
+
+    // Test that 24 will choose 23.976 if 24 is not supported
+    {
+        android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.desiredRefreshRate = Fps(24.f);
+        lr.name = "ExplicitExactOrMultiple 24 fps";
+        EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
+    }
+
+    // Test that 29.97 will prefer 59.94 over 60 and 30
+    {
+        android::DisplayModes modes = {mConfig24, mConfig24Frac, mConfig25,
+                                       mConfig30, mConfig60,     mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.desiredRefreshRate = Fps(29.97f);
+        lr.name = "ExplicitExactOrMultiple 29.97f fps";
+        EXPECT_EQ(HWC_CONFIG_ID_60_FRAC,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    // Test that voting for supported refresh rate will select this refresh rate
+    {
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+                                                     /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+        for (auto desiredRefreshRate : {23.976f, 24.f, 25.f, 29.97f, 30.f, 50.f, 59.94f, 60.f}) {
+            lr.vote = LayerVoteType::ExplicitExact;
+            lr.desiredRefreshRate = Fps(desiredRefreshRate);
+            std::stringstream ss;
+            ss << "ExplicitExact " << desiredRefreshRate << " fps";
+            lr.name = ss.str();
+
+            auto selecteRefreshRate =
+                    refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+
+            EXPECT_TRUE(selecteRefreshRate.getFps().equalsWithMargin(lr.desiredRefreshRate))
+                    << "Expecting " << lr.desiredRefreshRate << " but it was "
+                    << selecteRefreshRate.getFps();
+        }
+    }
+
+    // Test that 23.976 will choose 24 if 23.976 is not supported
+    {
+        android::DisplayModes modes = {mConfig24,     mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.vote = LayerVoteType::ExplicitExact;
+        lr.desiredRefreshRate = Fps(23.976f);
+        lr.name = "ExplicitExact 23.976 fps";
+        EXPECT_EQ(HWC_CONFIG_ID_24,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
+    }
+
+    // Test that 24 will choose 23.976 if 24 is not supported
+    {
+        android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.desiredRefreshRate = Fps(24.f);
+        lr.name = "ExplicitExact 24 fps";
+        EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
     }
 }
 
@@ -2028,6 +2147,29 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 0.5f},
+                                                LayerRequirement{.weight = 0.5f}};
+    auto& explicitDefaultLayer = layers[0];
+    auto& explicitExactOrMultipleLayer = layers[1];
+
+    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+    explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+
+    explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault;
+    explicitDefaultLayer.name = "ExplicitDefault";
+    explicitDefaultLayer.desiredRefreshRate = Fps(59.94f);
+
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
 TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
     EXPECT_TRUE(mExpected60Config < mExpected90Config);
     EXPECT_FALSE(mExpected60Config < mExpected60Config);
@@ -2116,7 +2258,38 @@
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f)));
-    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.6f)));
+
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(25.f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(23.976f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(30.f), Fps(29.97f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(60.f), Fps(59.94f)));
+}
+
+TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) {
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(23.976f), Fps(24.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(24.f), Fps(23.976f)));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(30.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(30.f), Fps(29.97f)));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(59.94f), Fps(60.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(60.f), Fps(59.94f)));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(60.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(60.f), Fps(29.97f)));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(59.94f), Fps(30.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(30.f), Fps(59.94f)));
+
+    const std::vector<float> refreshRates = {23.976f, 24.f, 25.f, 29.97f, 30.f, 50.f, 59.94f, 60.f};
+    for (auto refreshRate : refreshRates) {
+        EXPECT_FALSE(
+                RefreshRateConfigs::isFractionalPairOrMultiple(Fps(refreshRate), Fps(refreshRate)));
+    }
+
+    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(24.f), Fps(25.f)));
+    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(23.978f), Fps(25.f)));
+    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(59.94f)));
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 12b155b..0a8759d 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -81,7 +81,7 @@
                                                        int64_t vsyncPeriod) {
     return DisplayMode::Builder(static_cast<hal::HWConfigId>(modeId.value()))
             .setId(modeId)
-            .setPhysicalDisplayId(PhysicalDisplayId(0))
+            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
             .setVsyncPeriod(static_cast<int32_t>(vsyncPeriod))
             .setGroup(group)
             .build();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 5713c2f..e2b3993 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -34,7 +34,7 @@
 namespace android {
 namespace {
 
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID(999);
+constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
 
 class SchedulerTest : public testing::Test {
 protected:
@@ -53,13 +53,13 @@
 
     const DisplayModePtr mode60 = DisplayMode::Builder(0)
                                           .setId(DisplayModeId(0))
-                                          .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                          .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
                                           .setVsyncPeriod(Fps(60.f).getPeriodNsecs())
                                           .setGroup(0)
                                           .build();
     const DisplayModePtr mode120 = DisplayMode::Builder(1)
                                            .setId(DisplayModeId(1))
-                                           .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                           .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
                                            .setVsyncPeriod(Fps(120.f).getPeriodNsecs())
                                            .setGroup(0)
                                            .build();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
similarity index 74%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index b713334..6959ee3 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -22,8 +22,7 @@
 namespace android {
 namespace {
 
-class HandleTransactionLockedTest : public DisplayTransactionTest {
-public:
+struct DisplayTransactionCommitTest : DisplayTransactionTest {
     template <typename Case>
     void setupCommonPreconditions();
 
@@ -55,7 +54,7 @@
 };
 
 template <typename Case>
-void HandleTransactionLockedTest::setupCommonPreconditions() {
+void DisplayTransactionCommitTest::setupCommonPreconditions() {
     // Wide color displays support is configured appropriately
     Case::WideColorSupport::injectConfigChange(this);
 
@@ -68,7 +67,7 @@
 }
 
 template <typename Case, bool connected>
-void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
+void DisplayTransactionCommitTest::expectHotplugReceived(mock::EventThread* eventThread) {
     const auto convert = [](auto physicalDisplayId) {
         return std::make_optional(DisplayId{physicalDisplayId});
     };
@@ -79,7 +78,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
+void DisplayTransactionCommitTest::setupCommonCallExpectationsForConnectProcessing() {
     Case::Display::setupHwcHotplugCallExpectations(this);
 
     Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
@@ -97,7 +96,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
+void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() {
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
 
     expectHotplugReceived<Case, false>(mEventThread);
@@ -105,7 +104,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
+void DisplayTransactionCommitTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
     // The display device should have been set up in the list of displays.
     ASSERT_TRUE(hasDisplayDevice(displayToken));
     const auto& device = getDisplayDevice(displayToken);
@@ -137,7 +136,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
+void DisplayTransactionCommitTest::verifyPhysicalDisplayIsConnected() {
     // HWComposer should have an entry for the display
     EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
@@ -150,14 +149,14 @@
     verifyDisplayIsConnected<Case>(displayToken);
 }
 
-void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
+void DisplayTransactionCommitTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
     EXPECT_FALSE(hasDisplayDevice(displayToken));
     EXPECT_FALSE(hasCurrentDisplayState(displayToken));
     EXPECT_FALSE(hasDrawingDisplayState(displayToken));
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::processesHotplugConnectCommon() {
+void DisplayTransactionCommitTest::processesHotplugConnectCommon() {
     // --------------------------------------------------------------------
     // Preconditions
 
@@ -174,7 +173,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -191,7 +190,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
+void DisplayTransactionCommitTest::ignoresHotplugConnectCommon() {
     // --------------------------------------------------------------------
     // Preconditions
 
@@ -203,7 +202,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -213,7 +212,7 @@
 }
 
 template <typename Case>
-void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
+void DisplayTransactionCommitTest::processesHotplugDisconnectCommon() {
     // --------------------------------------------------------------------
     // Preconditions
 
@@ -238,7 +237,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -255,18 +254,18 @@
     verifyDisplayIsNotConnected(existing.token());
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectPrimaryDisplay) {
     processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectExternalDisplay) {
     // Inject a primary display.
     PrimaryDisplayVariant::injectHwcDisplay(this);
 
     processesHotplugConnectCommon<SimpleExternalDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
+TEST_F(DisplayTransactionCommitTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
     // Inject both a primary and external display.
     PrimaryDisplayVariant::injectHwcDisplay(this);
     ExternalDisplayVariant::injectHwcDisplay(this);
@@ -281,108 +280,119 @@
     ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
-    processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectPrimaryDisplay) {
+    EXPECT_EXIT(processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(),
+                testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectExternalDisplay) {
     processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
-    using Case = SimplePrimaryDisplayCase;
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimary) {
+    EXPECT_EXIT(
+            [this] {
+                using Case = SimplePrimaryDisplayCase;
 
-    // --------------------------------------------------------------------
-    // Preconditions
+                // --------------------------------------------------------------------
+                // Preconditions
 
-    setupCommonPreconditions<Case>();
+                setupCommonPreconditions<Case>();
 
-    // A hotplug connect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-    // A hotplug disconnect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+                // A hotplug connect event is enqueued for a display
+                Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+                // A hotplug disconnect event is also enqueued for the same display
+                Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
 
-    // --------------------------------------------------------------------
-    // Call Expectations
+                // --------------------------------------------------------------------
+                // Call Expectations
 
-    setupCommonCallExpectationsForConnectProcessing<Case>();
-    setupCommonCallExpectationsForDisconnectProcessing<Case>();
+                setupCommonCallExpectationsForConnectProcessing<Case>();
+                setupCommonCallExpectationsForDisconnectProcessing<Case>();
 
-    EXPECT_CALL(*mComposer,
-                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+                EXPECT_CALL(*mComposer,
+                            setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
+                                            IComposerClient::Vsync::DISABLE))
+                        .WillOnce(Return(Error::NONE));
+                EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
 
-    // --------------------------------------------------------------------
-    // Invocation
+                // --------------------------------------------------------------------
+                // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+                mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
-    // --------------------------------------------------------------------
-    // Postconditions
+                // --------------------------------------------------------------------
+                // Postconditions
 
-    // HWComposer should not have an entry for the display
-    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+                // HWComposer should not have an entry for the display
+                EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-    // SF should not have a display token.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+                // SF should not have a display token.
+                const auto displayId = Case::Display::DISPLAY_ID::get();
+                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+                ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+            }(),
+            testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
 
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
-    using Case = SimplePrimaryDisplayCase;
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectThenConnectPrimary) {
+    EXPECT_EXIT(
+            [this] {
+                using Case = SimplePrimaryDisplayCase;
 
-    // --------------------------------------------------------------------
-    // Preconditions
+                // --------------------------------------------------------------------
+                // Preconditions
 
-    setupCommonPreconditions<Case>();
+                setupCommonPreconditions<Case>();
 
-    // The display is already completely set up.
-    Case::Display::injectHwcDisplay(this);
-    auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
-    existing.inject();
+                // The display is already completely set up.
+                Case::Display::injectHwcDisplay(this);
+                auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+                existing.inject();
 
-    // A hotplug disconnect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-    // A hotplug connect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+                // A hotplug disconnect event is enqueued for a display
+                Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+                // A hotplug connect event is also enqueued for the same display
+                Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
 
-    // --------------------------------------------------------------------
-    // Call Expectations
+                // --------------------------------------------------------------------
+                // Call Expectations
 
-    setupCommonCallExpectationsForConnectProcessing<Case>();
-    setupCommonCallExpectationsForDisconnectProcessing<Case>();
+                setupCommonCallExpectationsForConnectProcessing<Case>();
+                setupCommonCallExpectationsForDisconnectProcessing<Case>();
 
-    // --------------------------------------------------------------------
-    // Invocation
+                // --------------------------------------------------------------------
+                // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+                mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
-    // --------------------------------------------------------------------
-    // Postconditions
+                // --------------------------------------------------------------------
+                // Postconditions
 
-    // The existing token should have been removed
-    verifyDisplayIsNotConnected(existing.token());
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
-    EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
+                // The existing token should have been removed
+                verifyDisplayIsNotConnected(existing.token());
+                const auto displayId = Case::Display::DISPLAY_ID::get();
+                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+                ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+                EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
 
-    // A new display should be connected in its place
+                // A new display should be connected in its place
 
-    verifyPhysicalDisplayIsConnected<Case>();
+                verifyPhysicalDisplayIsConnected<Case>();
 
-    // --------------------------------------------------------------------
-    // Cleanup conditions
+                // --------------------------------------------------------------------
+                // Cleanup conditions
 
-    EXPECT_CALL(*mComposer,
-                setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+                EXPECT_CALL(*mComposer,
+                            setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
+                                            IComposerClient::Vsync::DISABLE))
+                        .WillOnce(Return(Error::NONE));
+                EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+            }(),
+            testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
 
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
+TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAdded) {
     using Case = HwcVirtualDisplayCase;
 
     // --------------------------------------------------------------------
@@ -433,7 +443,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -453,7 +463,7 @@
     mFlinger.mutableDrawingState().displays.removeItem(displayToken);
 }
 
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
+TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAddedWithNoSurface) {
     using Case = HwcVirtualDisplayCase;
 
     // --------------------------------------------------------------------
@@ -479,7 +489,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -493,7 +503,7 @@
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
 }
 
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
+TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayRemoval) {
     using Case = HwcVirtualDisplayCase;
 
     // --------------------------------------------------------------------
@@ -511,7 +521,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -520,11 +530,11 @@
     verifyDisplayIsNotConnected(existing.token());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
-    constexpr uint32_t oldLayerStack = 0u;
-    constexpr uint32_t newLayerStack = 123u;
+    constexpr ui::LayerStack oldLayerStack = ui::DEFAULT_LAYER_STACK;
+    constexpr ui::LayerStack newLayerStack{123u};
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -540,7 +550,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -548,7 +558,7 @@
     EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayTransformChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     constexpr ui::Rotation oldTransform = ui::ROTATION_0;
@@ -568,7 +578,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -576,7 +586,7 @@
     EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackRectChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     const Rect oldLayerStackRect(0, 0, 0, 0);
@@ -596,7 +606,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -604,7 +614,7 @@
     EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayRectChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     const Rect oldDisplayRect(0, 0);
@@ -624,7 +634,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -632,7 +642,7 @@
     EXPECT_EQ(newDisplayRect, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayWidthChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     constexpr int oldWidth = 0;
@@ -674,10 +684,10 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayHeightChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     constexpr int oldWidth = 0;
@@ -719,10 +729,10 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
     constexpr uint32_t kOldWidth = 567;
@@ -769,7 +779,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
 
     EXPECT_EQ(display.mutableDisplayDevice()->getBounds(), kNewSize);
     EXPECT_EQ(display.mutableDisplayDevice()->getWidth(), kNewWidth);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
index ef8b149..bafa910 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -63,15 +63,11 @@
     // The primary display should have a current state
     ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
     const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
-    // The layer stack state should be set to zero
-    EXPECT_EQ(0u, primaryDisplayState.layerStack);
-    // The orientation state should be set to zero
+
+    // The primary display state should be reset
+    EXPECT_EQ(ui::DEFAULT_LAYER_STACK, primaryDisplayState.layerStack);
     EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
-
-    // The orientedDisplaySpaceRect state should be set to INVALID
     EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
-
-    // The layerStackSpaceRect state should be set to INVALID
     EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
 
     // The width and height should both be zero
@@ -99,4 +95,4 @@
 }
 
 } // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
index fc40818..7d9e22b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
@@ -25,6 +25,8 @@
 namespace android {
 namespace {
 
+constexpr ui::LayerStack LAYER_STACK{456u};
+
 class SetDisplayStateLockedTest : public DisplayTransactionTest {};
 
 TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
@@ -38,7 +40,7 @@
     DisplayState state;
     state.what = DisplayState::eLayerStackChanged;
     state.token = displayToken;
-    state.layerStack = 456;
+    state.layerStack = LAYER_STACK;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -167,13 +169,13 @@
     display.inject();
 
     // The display has a layer stack set
-    display.mutableCurrentDisplayState().layerStack = 456u;
+    display.mutableCurrentDisplayState().layerStack = LAYER_STACK;
 
     // The incoming request sets the same layer stack
     DisplayState state;
     state.what = DisplayState::eLayerStackChanged;
     state.token = display.token();
-    state.layerStack = 456u;
+    state.layerStack = LAYER_STACK;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -187,7 +189,7 @@
     EXPECT_EQ(0u, flags);
 
     // The current display state is unchanged
-    EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+    EXPECT_EQ(LAYER_STACK, display.getCurrentDisplayState().layerStack);
 }
 
 TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
@@ -201,13 +203,13 @@
     display.inject();
 
     // The display has a layer stack set
-    display.mutableCurrentDisplayState().layerStack = 654u;
+    display.mutableCurrentDisplayState().layerStack = ui::LayerStack{LAYER_STACK.id + 1};
 
     // The incoming request sets a different layer stack
     DisplayState state;
     state.what = DisplayState::eLayerStackChanged;
     state.token = display.token();
-    state.layerStack = 456u;
+    state.layerStack = LAYER_STACK;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -221,7 +223,7 @@
     EXPECT_EQ(eDisplayTransactionNeeded, flags);
 
     // The desired display state has been set to the new value.
-    EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+    EXPECT_EQ(LAYER_STACK, display.getCurrentDisplayState().layerStack);
 }
 
 TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfFlagsNotChanged) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index e32c4bf..7ead0af 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -292,7 +292,9 @@
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
-    setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
+    // External displays must be secondary, as the primary display cannot be disconnected.
+    EXPECT_EXIT(setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>(),
+                testing::KilledBySignal(SIGABRT), "Missing primary display");
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 40c881e..d8352ed 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -209,7 +209,7 @@
                         ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) {
         DisplayModes modes{DisplayMode::Builder(0)
                                    .setId(DisplayModeId(0))
-                                   .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                   .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
                                    .setVsyncPeriod(16'666'667)
                                    .setGroup(0)
                                    .build()};
@@ -217,7 +217,7 @@
         if (hasMultipleModes) {
             modes.emplace_back(DisplayMode::Builder(1)
                                        .setId(DisplayModeId(1))
-                                       .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                       .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
                                        .setVsyncPeriod(11'111'111)
                                        .setGroup(0)
                                        .build());
@@ -316,9 +316,9 @@
                                                        dispSurface, producer);
     }
 
-    auto handleTransactionLocked(uint32_t transactionFlags) {
-        Mutex::Autolock _l(mFlinger->mStateLock);
-        return mFlinger->handleTransactionLocked(transactionFlags);
+    auto commitTransactionsLocked(uint32_t transactionFlags) {
+        Mutex::Autolock lock(mFlinger->mStateLock);
+        return mFlinger->commitTransactionsLocked(transactionFlags);
     }
 
     void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) {
@@ -326,7 +326,7 @@
     }
 
     auto setDisplayStateLocked(const DisplayState& s) {
-        Mutex::Autolock _l(mFlinger->mStateLock);
+        Mutex::Autolock lock(mFlinger->mStateLock);
         return mFlinger->setDisplayStateLocked(s);
     }
 
@@ -426,7 +426,7 @@
     auto& mutableDisplays() { return mFlinger->mDisplays; }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
     auto& mutableEventQueue() { return mFlinger->mEventQueue; }
-    auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; }
+    auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
     auto& mutableInterceptor() { return mFlinger->mInterceptor; }
     auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
     auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
@@ -439,8 +439,7 @@
 
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
-    auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
-    auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
+    auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
     auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
 
     auto fromHandle(const sp<IBinder>& handle) {
@@ -600,13 +599,12 @@
                 LOG_ALWAYS_FATAL_IF(!physicalId);
                 flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
                 if (mIsPrimary) {
-                    flinger->mutableInternalHwcDisplayId() = mHwcDisplayId;
+                    flinger->mutablePrimaryHwcDisplayId() = mHwcDisplayId;
                 } else {
-                    // If there is an external HWC display there should always be an internal ID
+                    // If there is an external HWC display, there should always be a primary ID
                     // as well. Set it to some arbitrary value.
-                    auto& internalId = flinger->mutableInternalHwcDisplayId();
-                    if (!internalId) internalId = mHwcDisplayId - 1;
-                    flinger->mutableExternalHwcDisplayId() = mHwcDisplayId;
+                    auto& primaryId = flinger->mutablePrimaryHwcDisplayId();
+                    if (!primaryId) primaryId = mHwcDisplayId - 1;
                 }
             }
         }
@@ -645,7 +643,7 @@
             DisplayModePtr activeMode =
                     DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
                             .setId(mActiveModeId)
-                            .setPhysicalDisplayId(PhysicalDisplayId(0))
+                            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
                             .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
                             .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
                             .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
@@ -764,9 +762,9 @@
     };
 
 private:
+    void scheduleRefresh(FrameHint) override {}
     void setVsyncEnabled(bool) override {}
     void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override {}
-    void repaintEverythingForHWC() override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() {}
 
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
index 7de1872..20d41e6 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
@@ -18,6 +18,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
+#undef LOG_TAG
 #define LOG_TAG "MockComposer"
 #include "mock/DisplayHardware/MockComposer.h"
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index ab19886..291559f 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -23,20 +23,20 @@
 namespace android::mock {
 
 struct SchedulerCallback final : ISchedulerCallback {
+    MOCK_METHOD(void, scheduleRefresh, (FrameHint), (override));
     MOCK_METHOD1(setVsyncEnabled, void(bool));
     MOCK_METHOD2(changeRefreshRate,
                  void(const scheduler::RefreshRateConfigs::RefreshRate&,
                       scheduler::RefreshRateConfigEvent));
-    MOCK_METHOD0(repaintEverythingForHWC, void());
     MOCK_METHOD1(kernelTimerChanged, void(bool));
     MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void());
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
+    void scheduleRefresh(FrameHint) override {}
     void setVsyncEnabled(bool) override {}
     void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
                            scheduler::RefreshRateConfigEvent) override {}
-    void repaintEverythingForHWC() override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() {}
 };
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 26052fb..33401d2 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -560,6 +560,7 @@
     }
 
     static const char* const known_non_device_names[] = {
+        "vkAcquireDrmDisplayEXT",
         "vkCreateAndroidSurfaceKHR",
         "vkCreateDebugReportCallbackEXT",
         "vkCreateDebugUtilsMessengerEXT",
@@ -581,6 +582,7 @@
         "vkEnumeratePhysicalDevices",
         "vkGetDisplayModeProperties2KHR",
         "vkGetDisplayPlaneCapabilities2KHR",
+        "vkGetDrmDisplayEXT",
         "vkGetInstanceProcAddr",
         "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT",
         "vkGetPhysicalDeviceDisplayPlaneProperties2KHR",
@@ -624,6 +626,8 @@
         "vkGetPhysicalDeviceSurfacePresentModesKHR",
         "vkGetPhysicalDeviceSurfaceSupportKHR",
         "vkGetPhysicalDeviceToolPropertiesEXT",
+        "vkGetPhysicalDeviceVideoCapabilitiesKHR",
+        "vkGetPhysicalDeviceVideoFormatPropertiesKHR",
         "vkSubmitDebugUtilsMessageEXT",
     };
     // clang-format on
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index d7fdab5..cf774fd 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -979,6 +979,8 @@
 void QueryPresentationProperties(
     VkPhysicalDevice physicalDevice,
     VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) {
+    ATRACE_CALL();
+
     // Request the android-specific presentation properties via GPDP2
     VkPhysicalDeviceProperties2 properties = {
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
@@ -994,7 +996,17 @@
     presentation_properties->pNext = nullptr;
     presentation_properties->sharedImage = VK_FALSE;
 
-    GetPhysicalDeviceProperties2(physicalDevice, &properties);
+    const auto& driver = GetData(physicalDevice).driver;
+
+    if (driver.GetPhysicalDeviceProperties2) {
+        // >= 1.1 driver, supports core GPDP2 entrypoint.
+        driver.GetPhysicalDeviceProperties2(physicalDevice, &properties);
+    } else if (driver.GetPhysicalDeviceProperties2KHR) {
+        // Old driver, but may support presentation properties
+        // if we have the GPDP2 extension. Otherwise, no presentation
+        // properties supported.
+        driver.GetPhysicalDeviceProperties2KHR(physicalDevice, &properties);
+    }
 }
 
 VkResult EnumerateDeviceExtensionProperties(
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 72fd4fb..4176509 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -33,6 +33,7 @@
     'VK_EXT_metal_surface',
     'VK_FUCHSIA_imagepipe_surface',
     'VK_GGP_stream_descriptor_surface',
+    'VK_HUAWEI_subpass_shading',
     'VK_KHR_display',
     'VK_KHR_display_swapchain',
     'VK_KHR_external_fence_win32',
@@ -47,11 +48,13 @@
     'VK_MVK_ios_surface',
     'VK_MVK_macos_surface',
     'VK_NN_vi_surface',
+    'VK_NV_acquire_winrt_display',
     'VK_NV_cooperative_matrix',
     'VK_NV_coverage_reduction_mode',
     'VK_NV_external_memory_win32',
     'VK_NV_win32_keyed_mutex',
     'VK_NVX_image_view_handle',
+    'VK_QNX_screen_surface',
 ]
 
 # Extensions having functions exported by the loader.