Merge "[native] Migrate to safer GrDirectContext::submit() API" into main
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 9847c05..613721e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -85,6 +85,7 @@
         changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
         defaultFrameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
         frameRateCategory(ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT),
+        frameRateSelectionStrategy(ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF),
         fixedTransformHint(ui::Transform::ROT_INVALID),
         autoRefresh(false),
         isTrustedOverlay(false),
@@ -161,6 +162,7 @@
     SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
     SAFE_PARCEL(output.writeByte, defaultFrameRateCompatibility);
     SAFE_PARCEL(output.writeByte, frameRateCategory);
+    SAFE_PARCEL(output.writeByte, frameRateSelectionStrategy);
     SAFE_PARCEL(output.writeUint32, fixedTransformHint);
     SAFE_PARCEL(output.writeBool, autoRefresh);
     SAFE_PARCEL(output.writeBool, dimmingEnabled);
@@ -294,6 +296,7 @@
     SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
     SAFE_PARCEL(input.readByte, &defaultFrameRateCompatibility);
     SAFE_PARCEL(input.readByte, &frameRateCategory);
+    SAFE_PARCEL(input.readByte, &frameRateSelectionStrategy);
     SAFE_PARCEL(input.readUint32, &tmpUint32);
     fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
     SAFE_PARCEL(input.readBool, &autoRefresh);
@@ -667,6 +670,10 @@
         what |= eFrameRateCategoryChanged;
         frameRateCategory = other.frameRateCategory;
     }
+    if (other.what & eFrameRateSelectionStrategyChanged) {
+        what |= eFrameRateSelectionStrategyChanged;
+        frameRateSelectionStrategy = other.frameRateSelectionStrategy;
+    }
     if (other.what & eFixedTransformHintChanged) {
         what |= eFixedTransformHintChanged;
         fixedTransformHint = other.fixedTransformHint;
@@ -778,6 +785,7 @@
     CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
                 changeFrameRateStrategy);
     CHECK_DIFF(diff, eFrameRateCategoryChanged, other, frameRateCategory);
+    CHECK_DIFF(diff, eFrameRateSelectionStrategyChanged, other, frameRateSelectionStrategy);
     CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint);
     CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
     CHECK_DIFF(diff, eTrustedOverlayChanged, other, isTrustedOverlay);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index e0882ac..038764b 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2105,6 +2105,19 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setFrameRateSelectionStrategy(const sp<SurfaceControl>& sc,
+                                                                  int8_t strategy) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eFrameRateSelectionStrategyChanged;
+    s->frameRateSelectionStrategy = strategy;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixedTransformHint(
         const sp<SurfaceControl>& sc, int32_t fixedTransformHint) {
     layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 102a3c1..4371007 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -197,7 +197,7 @@
         eInputInfoChanged = 0x40000000,
         eCornerRadiusChanged = 0x80000000,
         eDestinationFrameChanged = 0x1'00000000,
-        /* unused = 0x2'00000000, */
+        eFrameRateSelectionStrategyChanged = 0x2'00000000,
         eBackgroundColorChanged = 0x4'00000000,
         eMetadataChanged = 0x8'00000000,
         eColorSpaceAgnosticChanged = 0x10'00000000,
@@ -268,6 +268,7 @@
             layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
             layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged |
             layer_state_t::eFrameRateChanged | layer_state_t::eFrameRateCategoryChanged |
+            layer_state_t::eFrameRateSelectionStrategyChanged |
             layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFixedTransformHintChanged;
 
     // Changes affecting data sent to input.
@@ -361,6 +362,9 @@
     // Frame rate category to suggest what frame rate range a surface should run.
     int8_t frameRateCategory;
 
+    // Strategy of the layer for frame rate selection.
+    int8_t frameRateSelectionStrategy;
+
     // Set by window manager indicating the layer and all its children are
     // in a different orientation than the display. The hint suggests that
     // the graphic producers should receive a transform hint as if the
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 6fef5d2..42e3c16 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -687,6 +687,8 @@
 
         Transaction& setFrameRateCategory(const sp<SurfaceControl>& sc, int8_t category);
 
+        Transaction& setFrameRateSelectionStrategy(const sp<SurfaceControl>& sc, int8_t strategy);
+
         // Set by window manager indicating the layer and all its children are
         // in a different orientation than the display. The hint suggests that
         // the graphic producers should receive a transform hint as if the
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index ee961f0..a0ec6ad 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -20,6 +20,7 @@
 #include <input/InputDevice.h>
 #include <input/KeyLayoutMap.h>
 #include <input/Keyboard.h>
+#include <linux/uinput.h>
 #include "android-base/file.h"
 
 namespace android {
@@ -97,7 +98,7 @@
     ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
 }
 
-TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) {
+TEST_F(InputDeviceKeyMapTest, keyCharacterMapApplyMultipleOverlaysTest) {
     std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm";
     std::string englishOverlayPath = base::GetExecutableDirectory() + "/data/english_us.kcm";
     std::string germanOverlayPath = base::GetExecutableDirectory() + "/data/german.kcm";
@@ -133,14 +134,33 @@
     ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
 }
 
-TEST_F(InputDeviceKeyMapTest, keyCharacteMapBadAxisLabel) {
+TEST_F(InputDeviceKeyMapTest, keyCharacterMapApplyOverlayTest) {
+    std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm";
+    base::Result<std::shared_ptr<KeyCharacterMap>> frenchOverlay =
+            KeyCharacterMap::load(frenchOverlayPath, KeyCharacterMap::Format::OVERLAY);
+    ASSERT_TRUE(frenchOverlay.ok()) << "Cannot load KeyCharacterMap at " << frenchOverlayPath;
+
+    // Apply the French overlay
+    mKeyMap.keyCharacterMap->combine(*frenchOverlay->get());
+
+    // Check if mapping for key_Q is correct
+    int32_t outKeyCode;
+    status_t mapKeyResult = mKeyMap.keyCharacterMap->mapKey(KEY_Q, /*usageCode=*/0, &outKeyCode);
+    ASSERT_EQ(mapKeyResult, OK) << "No mapping for KEY_Q for " << frenchOverlayPath;
+    ASSERT_EQ(outKeyCode, AKEYCODE_A);
+
+    mapKeyResult = mKeyMap.keyCharacterMap->mapKey(KEY_E, /*usageCode=*/0, &outKeyCode);
+    ASSERT_NE(mapKeyResult, OK) << "Mapping exists for KEY_E for " << frenchOverlayPath;
+}
+
+TEST_F(InputDeviceKeyMapTest, keyCharacterMapBadAxisLabel) {
     std::string klPath = base::GetExecutableDirectory() + "/data/bad_axis_label.kl";
 
     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
     ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
 }
 
-TEST_F(InputDeviceKeyMapTest, keyCharacteMapBadLedLabel) {
+TEST_F(InputDeviceKeyMapTest, keyCharacterMapBadLedLabel) {
     std::string klPath = base::GetExecutableDirectory() + "/data/bad_led_label.kl";
 
     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index e158f01..b068f48 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1096,6 +1096,26 @@
     ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 4
 };
 
+/*
+ * Frame rate selection strategy values that can be used in
+ * Transaction::setFrameRateSelectionStrategy.
+ */
+enum {
+    /**
+     * Default value. The layer uses its own frame rate specifications, assuming it has any
+     * specifications, instead of its parent's.
+     */
+    ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF = 0,
+
+    /**
+     * The layer's frame rate specifications will propagate to and override those of its descendant
+     * layers.
+     * The layer with this strategy has the ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF
+     * behavior for itself.
+     */
+    ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN = 1,
+};
+
 static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
                                         int8_t compatibility, int8_t changeFrameRateStrategy) {
     return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index cb369a8..fa8f548 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -269,6 +269,11 @@
     return msg;
 }
 
+std::ostream& operator<<(std::ostream& out, const MotionEntry& motionEntry) {
+    out << motionEntry.getDescription();
+    return out;
+}
+
 // --- SensorEntry ---
 
 SensorEntry::SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 8dc2a2a..dd4aab8 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -24,6 +24,7 @@
 #include <stdint.h>
 #include <utils/Timers.h>
 #include <functional>
+#include <ostream>
 #include <string>
 
 namespace android::inputdispatcher {
@@ -189,6 +190,8 @@
     ~MotionEntry() override;
 };
 
+std::ostream& operator<<(std::ostream& out, const MotionEntry& motionEntry);
+
 struct SensorEntry : EventEntry {
     int32_t deviceId;
     uint32_t source;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 4221e42..9dcf615 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -288,4 +288,9 @@
     return out;
 }
 
+std::ostream& operator<<(std::ostream& out, const TouchState& state) {
+    out << state.dump();
+    return out;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 39e63e5..f016936 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <bitset>
+#include <ostream>
 #include <set>
 #include "TouchedWindow.h"
 
@@ -79,5 +80,7 @@
     std::string dump() const;
 };
 
+std::ostream& operator<<(std::ostream& out, const TouchState& state);
+
 } // namespace inputdispatcher
 } // namespace android
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 9807a6d..ff4b425 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -256,5 +256,10 @@
     return out;
 }
 
+std::ostream& operator<<(std::ostream& out, const TouchedWindow& window) {
+    out << window.dump();
+    return out;
+}
+
 } // namespace inputdispatcher
 } // namespace android
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 0a38f9f..3f760c0 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -20,6 +20,7 @@
 #include <input/Input.h>
 #include <utils/BitSet.h>
 #include <bitset>
+#include <ostream>
 #include <set>
 #include "InputTarget.h"
 
@@ -92,5 +93,7 @@
     static std::string deviceStateToString(const TouchedWindow::DeviceState& state);
 };
 
+std::ostream& operator<<(std::ostream& out, const TouchedWindow& window);
+
 } // namespace inputdispatcher
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 22db247..fe56969 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -844,10 +844,16 @@
 
 bool OutputLayer::needsFiltering() const {
     const auto& state = getState();
-    const auto& displayFrame = state.displayFrame;
     const auto& sourceCrop = state.sourceCrop;
-    return sourceCrop.getHeight() != displayFrame.getHeight() ||
-            sourceCrop.getWidth() != displayFrame.getWidth();
+    auto displayFrameWidth = static_cast<float>(state.displayFrame.getWidth());
+    auto displayFrameHeight = static_cast<float>(state.displayFrame.getHeight());
+
+    if (state.bufferTransform & HAL_TRANSFORM_ROT_90) {
+        std::swap(displayFrameWidth, displayFrameHeight);
+    }
+
+    return sourceCrop.getHeight() != displayFrameHeight ||
+            sourceCrop.getWidth() != displayFrameWidth;
 }
 
 std::optional<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionSettings() const {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 9039d16..630906a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -1614,5 +1614,20 @@
     EXPECT_TRUE(mOutputLayer.needsFiltering());
 }
 
+TEST_F(OutputLayerTest, needsFilteringReturnsFalseIfRotatedDisplaySizeSameAsSourceSize) {
+    mOutputLayer.editState().displayFrame = Rect(100, 100, 300, 200);
+    mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.f, 200.f};
+    mOutputLayer.editState().bufferTransform = Hwc2::Transform::ROT_90;
+
+    EXPECT_FALSE(mOutputLayer.needsFiltering());
+}
+
+TEST_F(OutputLayerTest, needsFilteringReturnsTrueIfRotatedDisplaySizeDiffersFromSourceSize) {
+    mOutputLayer.editState().displayFrame = Rect(100, 100, 300, 200);
+    mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.f, 200.f};
+
+    EXPECT_TRUE(mOutputLayer.needsFiltering());
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 7537a39..a5e9368 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -85,6 +85,7 @@
     bool isTrustedOverlay;
     gui::GameMode gameMode;
     scheduler::LayerInfo::FrameRate frameRate;
+    scheduler::LayerInfo::FrameRateSelectionStrategy frameRateSelectionStrategy;
     ui::Transform::RotationFlags fixedTransformHint;
     std::optional<ui::Transform::RotationFlags> transformHint;
     bool handleSkipScreenshotFlag = false;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index da84e44..4c9fb06 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -813,11 +813,23 @@
                 RequestedLayerState::Changes::Hierarchy) ||
         snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
                              RequestedLayerState::Changes::Hierarchy)) {
-        snapshot.frameRate = requested.requestedFrameRate.isValid() ? requested.requestedFrameRate
-                                                                    : parentSnapshot.frameRate;
+        bool shouldOverrideChildren = parentSnapshot.frameRateSelectionStrategy ==
+                scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren;
+        snapshot.frameRate = !requested.requestedFrameRate.isValid() || shouldOverrideChildren
+                ? parentSnapshot.frameRate
+                : requested.requestedFrameRate;
         snapshot.changes |= RequestedLayerState::Changes::FrameRate;
     }
 
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionStrategyChanged) {
+        const auto strategy = scheduler::LayerInfo::convertFrameRateSelectionStrategy(
+                requested.frameRateSelectionStrategy);
+        snapshot.frameRateSelectionStrategy =
+                strategy == scheduler::LayerInfo::FrameRateSelectionStrategy::Self
+                ? parentSnapshot.frameRateSelectionStrategy
+                : strategy;
+    }
+
     if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionPriority) {
         snapshot.frameRateSelectionPriority =
                 (requested.frameRateSelectionPriority == Layer::PRIORITY_UNSET)
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 2bf8bc9..fcc1e61 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -125,6 +125,8 @@
     defaultFrameRateCompatibility =
             static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
     frameRateCategory = static_cast<int8_t>(FrameRateCategory::Default);
+    frameRateSelectionStrategy =
+            static_cast<int8_t>(scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
     dataspace = ui::Dataspace::V0_SRGB;
     gameMode = gui::GameMode::Unsupported;
     requestedFrameRate = {};
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2684000..31b5e28 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -192,6 +192,7 @@
     mDrawingState.dropInputMode = gui::DropInputMode::NONE;
     mDrawingState.dimmingEnabled = true;
     mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default;
+    mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Self;
 
     if (args.flags & ISurfaceComposerClient::eNoColorFill) {
         // Set an invalid color so there is no color fill.
@@ -1338,6 +1339,15 @@
     return true;
 }
 
+bool Layer::setFrameRateSelectionStrategy(FrameRateSelectionStrategy strategy) {
+    if (mDrawingState.frameRateSelectionStrategy == strategy) return false;
+    mDrawingState.frameRateSelectionStrategy = strategy;
+    mDrawingState.sequence++;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
                                                       nsecs_t postTime) {
     mDrawingState.postTime = postTime;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 0b0cef5..d5e2185 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -112,6 +112,7 @@
 
     using FrameRate = scheduler::LayerInfo::FrameRate;
     using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+    using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
 
     struct State {
         int32_t z;
@@ -188,6 +189,8 @@
         // The combined frame rate of parents / children of this layer
         FrameRate frameRateForLayerTree;
 
+        FrameRateSelectionStrategy frameRateSelectionStrategy;
+
         // Set by window manager indicating the layer and all its children are
         // in a different orientation than the display. The hint suggests that
         // the graphic producers should receive a transform hint as if the
@@ -782,6 +785,8 @@
     bool setFrameRate(FrameRate::FrameRateVote);
     bool setFrameRateCategory(FrameRateCategory);
 
+    bool setFrameRateSelectionStrategy(FrameRateSelectionStrategy);
+
     virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {}
     void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime);
     void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 0784251..03844ef 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -490,6 +490,19 @@
     }
 }
 
+LayerInfo::FrameRateSelectionStrategy LayerInfo::convertFrameRateSelectionStrategy(
+        int8_t strategy) {
+    switch (strategy) {
+        case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF:
+            return FrameRateSelectionStrategy::Self;
+        case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN:
+            return FrameRateSelectionStrategy::OverrideChildren;
+        default:
+            LOG_ALWAYS_FATAL("Invalid frame rate selection strategy value %d", strategy);
+            return FrameRateSelectionStrategy::Self;
+    }
+}
+
 bool LayerInfo::FrameRate::isNoVote() const {
     return vote.type == FrameRateCompatibility::NoVote;
 }
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 7fe407f..3b4d823 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -96,6 +96,13 @@
         ftl_last = NoVote
     };
 
+    enum class FrameRateSelectionStrategy {
+        Self,
+        OverrideChildren,
+
+        ftl_last = OverrideChildren
+    };
+
     // Encapsulates the frame rate specifications of the layer. This information will be used
     // when the display refresh rate is determined.
     struct FrameRate {
@@ -139,11 +146,11 @@
         static FrameRateCompatibility convertCompatibility(int8_t compatibility);
 
         // Convert an ANATIVEWINDOW_CHANGE_FRAME_RATE_* value to a scheduler::Seamlessness.
-        // Logs fatal if the compatibility value is invalid.
+        // Logs fatal if the strategy value is invalid.
         static scheduler::Seamlessness convertChangeFrameRateStrategy(int8_t strategy);
 
         // Convert an ANATIVEWINDOW_FRAME_RATE_CATEGORY_* value to a FrameRateCategory.
-        // Logs fatal if the compatibility value is invalid.
+        // Logs fatal if the category value is invalid.
         static FrameRateCategory convertCategory(int8_t category);
 
         // True if the FrameRate has explicit frame rate specifications.
@@ -164,6 +171,10 @@
         }
     };
 
+    // Convert an ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_* value to FrameRateSelectionStrategy.
+    // Logs fatal if the strategy value is invalid.
+    static FrameRateSelectionStrategy convertFrameRateSelectionStrategy(int8_t strategy);
+
     static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
 
     LayerInfo(const std::string& name, uid_t ownerUid, LayerHistory::LayerVoteType defaultVote);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f1a45cb..fadde51 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -5206,6 +5206,14 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eFrameRateSelectionStrategyChanged) {
+        const scheduler::LayerInfo::FrameRateSelectionStrategy strategy =
+                scheduler::LayerInfo::convertFrameRateSelectionStrategy(
+                        s.frameRateSelectionStrategy);
+        if (layer->setFrameRateSelectionStrategy(strategy)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     if (what & layer_state_t::eFixedTransformHintChanged) {
         if (layer->setFixedTransformHint(s.fixedTransformHint)) {
             flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index acfde71..d7ac038 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -346,6 +346,18 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what =
+                layer_state_t::eFrameRateSelectionStrategyChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.frameRateSelectionStrategy = strategy;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     void setRoundedCorners(uint32_t id, float radius) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 767dd4f..1a9233d 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -743,6 +743,50 @@
     EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
 }
 
+TEST_F(LayerSnapshotTest, frameRateSelectionStrategy) {
+    // ROOT
+    // ├── 1
+    // │   ├── 11
+    // │   │   └── 111
+    // │   ├── 12 (frame rate set to 244.f with strategy OverrideChildren)
+    // │   │   ├── 121
+    // │   │   └── 122 (frame rate set to 123.f but should be overridden by layer 12)
+    // │   │       └── 1221
+    // │   └── 13
+    // └── 2
+    setFrameRate(12, 244.f, 0, 0);
+    setFrameRate(122, 123.f, 0, 0);
+    setFrameRateSelectionStrategy(12, 1 /* OverrideChildren */);
+
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    // verify parent 1 gets no vote
+    EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+              scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+    EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.rate.getValue(), 244.f);
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+    EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.rate.getValue(), 244.f);
+    EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+    EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f);
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+    EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 244.f);
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+    EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+}
+
 TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) {
     setRoundedCorners(1, 42.f);
     setRoundedCorners(2, 42.f);
diff --git a/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h b/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
index 8e28a75..11723c7 100644
--- a/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
+++ b/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android-base/properties.h>
 #include <gtest/gtest.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/android_filesystem_config.h>
@@ -21,7 +22,8 @@
 #include <future>
 
 namespace android {
-using Transaction = SurfaceComposerClient::Transaction;
+
+using base::HwTimeoutMultiplier;
 using gui::DisplayInfo;
 using gui::WindowInfo;
 
@@ -36,7 +38,8 @@
         auto listener = sp<WindowInfosListener>::make(std::move(predicate), promise);
         mClient->addWindowInfosListener(listener);
         auto future = promise.get_future();
-        bool satisfied = future.wait_for(std::chrono::seconds{1}) == std::future_status::ready;
+        bool satisfied = future.wait_for(std::chrono::seconds{5 * HwTimeoutMultiplier()}) ==
+                std::future_status::ready;
         mClient->removeWindowInfosListener(listener);
         return satisfied;
     }