Merge changes I30c34f13,I36e2e132 into main
* changes:
[SF] Update only the render rate on the refresh rate indicator
[SF] Add sysprop to enable vrr config
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 589df9a..ef0ae4f 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -54,6 +54,11 @@
// Another arbitrary value a binder count needs to drop below before another callback will be called
uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000;
+std::atomic<uint32_t> BpBinder::sBinderProxyCount(0);
+std::atomic<uint32_t> BpBinder::sBinderProxyCountWarned(0);
+
+static constexpr uint32_t kBinderProxyCountWarnInterval = 5000;
+
// Log any transactions for which the data exceeds this size
#define LOG_TRANSACTIONS_OVER_SIZE (300 * 1024)
@@ -193,6 +198,18 @@
}
sTrackingMap[trackedUid]++;
}
+ uint32_t numProxies = sBinderProxyCount.fetch_add(1, std::memory_order_relaxed);
+ uint32_t numLastWarned = sBinderProxyCountWarned.load(std::memory_order_relaxed);
+ uint32_t numNextWarn = numLastWarned + kBinderProxyCountWarnInterval;
+ if (numProxies >= numNextWarn) {
+ // Multiple threads can get here, make sure only one of them gets to
+ // update the warn counter.
+ if (sBinderProxyCountWarned.compare_exchange_strong(numLastWarned,
+ numNextWarn,
+ std::memory_order_relaxed)) {
+ ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies);
+ }
+ }
return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);
}
@@ -604,6 +621,7 @@
}
}
}
+ --sBinderProxyCount;
if (ipc) {
ipc->expungeHandle(binderHandle(), this);
@@ -688,6 +706,11 @@
return 0;
}
+uint32_t BpBinder::getBinderProxyCount()
+{
+ return sBinderProxyCount.load();
+}
+
void BpBinder::getCountByUid(Vector<uint32_t>& uids, Vector<uint32_t>& counts)
{
AutoMutex _l(sTrackingLock);
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 5496d61..a5c6094 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -87,6 +87,7 @@
static void setCountByUidEnabled(bool enable);
static void setLimitCallback(binder_proxy_limit_callback cb);
static void setBinderProxyCountWatermarks(int high, int low);
+ static uint32_t getBinderProxyCount();
std::optional<int32_t> getDebugBinderHandle() const;
@@ -208,6 +209,8 @@
static uint32_t sBinderProxyCountLowWatermark;
static bool sBinderProxyThrottleCreate;
static std::unordered_map<int32_t,uint32_t> sLastLimitCallbackMap;
+ static std::atomic<uint32_t> sBinderProxyCount;
+ static std::atomic<uint32_t> sBinderProxyCountWarned;
};
} // namespace android
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 76c7aac..4786c89 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -25,6 +25,7 @@
#pragma once
+#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp
index 8ee396e..8fb755c 100644
--- a/libs/binder/ndk/tests/Android.bp
+++ b/libs/binder/ndk/tests/Android.bp
@@ -80,6 +80,28 @@
require_root: true,
}
+cc_test_host {
+ name: "libbinder_ndk_unit_test_host",
+ defaults: ["test_libbinder_ndk_defaults"],
+ srcs: ["libbinder_ndk_unit_test_host.cpp"],
+ test_suites: [
+ "general-tests",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+ static_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libbinder",
+ "libcutils",
+ "libfakeservicemanager",
+ "libgmock",
+ "liblog",
+ "libutils",
+ ],
+}
+
cc_test {
name: "binderVendorDoubleLoadTest",
vendor: true,
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 3724fa1..15708ca 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -39,7 +39,6 @@
#include <condition_variable>
#include <iostream>
#include <mutex>
-#include <optional>
#include <thread>
#include "android/binder_ibinder.h"
@@ -433,22 +432,6 @@
EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
}
-// TEST(NdkBinder, IsUpdatable) {
-// bool isUpdatable =
-// AServiceManager_isUpdatableViaApex("android.hardware.light.ILights/default");
-// EXPECT_EQ(isUpdatable, true);
-// }
-//
-// TEST(NdkBinder, GetUpdatableViaApex) {
-// std::optional<std::string> updatableViaApex;
-// AServiceManager_getUpdatableApexName(
-// "android.hardware.light.ILights/default", &updatableViaApex,
-// [](const char* apexName, void* context) {
-// *static_cast<std::optional<std::string>*>(context) = apexName;
-// });
-// EXPECT_NE(updatableViaApex, std::nullopt) << *updatableViaApex;
-// }
-
// This is too slow
TEST(NdkBinder, CheckLazyServiceShutDown) {
ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test_host.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test_host.cpp
new file mode 100644
index 0000000..0a3021d
--- /dev/null
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test_host.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+#include <optional>
+
+#include "fakeservicemanager/FakeServiceManager.h"
+
+using android::FakeServiceManager;
+using android::setDefaultServiceManager;
+using android::sp;
+using android::String16;
+using android::String8;
+using testing::_;
+using testing::Eq;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Optional;
+using testing::Return;
+
+struct MockServiceManager : FakeServiceManager {
+ MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&));
+};
+
+struct AServiceManager : testing::Test {
+ static sp<MockServiceManager> mockSM;
+
+ static void InitMock() {
+ mockSM = new NiceMock<MockServiceManager>;
+ setDefaultServiceManager(mockSM);
+ }
+
+ void TearDown() override { Mock::VerifyAndClear(mockSM.get()); }
+
+ void ExpectUpdatableViaApexReturns(std::optional<String16> apexName) {
+ EXPECT_CALL(*mockSM, updatableViaApex(_)).WillRepeatedly(Return(apexName));
+ }
+};
+
+sp<MockServiceManager> AServiceManager::mockSM;
+
+TEST_F(AServiceManager, isUpdatableViaApex) {
+ auto apexFoo = String16("com.android.hardware.foo");
+ ExpectUpdatableViaApexReturns(apexFoo);
+
+ bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.foo.IFoo/default");
+ EXPECT_EQ(isUpdatable, true);
+}
+
+TEST_F(AServiceManager, isUpdatableViaApex_Not) {
+ ExpectUpdatableViaApexReturns(std::nullopt);
+
+ bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.foo.IFoo/default");
+ EXPECT_EQ(isUpdatable, false);
+}
+
+void getUpdatableApexNameCallback(const char* apexName, void* context) {
+ *(static_cast<std::optional<std::string>*>(context)) = apexName;
+}
+
+TEST_F(AServiceManager, getUpdatableApexName) {
+ auto apexFoo = String16("com.android.hardware.foo");
+ ExpectUpdatableViaApexReturns(apexFoo);
+
+ std::optional<std::string> result;
+ AServiceManager_getUpdatableApexName("android.hardware.foo.IFoo/default", &result,
+ getUpdatableApexNameCallback);
+ EXPECT_THAT(result, Optional(std::string(String8(apexFoo))));
+}
+
+TEST_F(AServiceManager, getUpdatableApexName_Null) {
+ ExpectUpdatableViaApexReturns(std::nullopt);
+
+ std::optional<std::string> result;
+ AServiceManager_getUpdatableApexName("android.hardware.foo.IFoo/default", &result,
+ getUpdatableApexNameCallback);
+ EXPECT_THAT(result, Eq(std::nullopt));
+}
+
+int main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ AServiceManager::InitMock();
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
index 09cb216..b80ac53 100644
--- a/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
@@ -16,6 +16,7 @@
#include <commonFuzzHelpers.h>
#include <fuzzer/FuzzedDataProvider.h>
+#include <functional>
#include <string>
#include <vector>
#include "BufferedTextOutput.h"
diff --git a/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp
index f835997..9df5632 100644
--- a/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp
+++ b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp
@@ -20,6 +20,7 @@
#include <fuzzer/FuzzedDataProvider.h>
#include <android-base/unique_fd.h>
#include <cputimeinstate.h>
+#include <functional>
using namespace android::bpf;
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index c8d1da7..412931b 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -205,6 +205,13 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_X, predictedPoint.x);
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, predictedPoint.y);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
+ // Copy forward tilt and orientation from the last event until they are predicted
+ // (b/291789258).
+ coords.setAxisValue(AMOTION_EVENT_AXIS_TILT,
+ event.getAxisValue(AMOTION_EVENT_AXIS_TILT, 0));
+ coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+ event.getRawPointerCoords(0)->getAxisValue(
+ AMOTION_EVENT_AXIS_ORIENTATION));
predictionTime += mModel->config().predictionInterval;
if (i == 0) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 0a1e889..b3be89e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -714,6 +714,40 @@
});
}
+/**
+ * In general, touch should be always split between windows. Some exceptions:
+ * 1. Don't split touch if all of the below is true:
+ * (a) we have an active pointer down *and*
+ * (b) a new pointer is going down that's from the same device *and*
+ * (c) the window that's receiving the current pointer does not support split touch.
+ * 2. Don't split mouse events
+ */
+bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) {
+ if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) {
+ // We should never split mouse events
+ return false;
+ }
+ for (const TouchedWindow& touchedWindow : touchState.windows) {
+ if (touchedWindow.windowHandle->getInfo()->isSpy()) {
+ // Spy windows should not affect whether or not touch is split.
+ continue;
+ }
+ if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) {
+ continue;
+ }
+ if (touchedWindow.windowHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
+ // Wallpaper window should not affect whether or not touch is split
+ continue;
+ }
+
+ if (touchedWindow.hasTouchingPointers(entry.deviceId)) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -2218,40 +2252,6 @@
return responsiveMonitors;
}
-/**
- * In general, touch should be always split between windows. Some exceptions:
- * 1. Don't split touch is if we have an active pointer down, and a new pointer is going down that's
- * from the same device, *and* the window that's receiving the current pointer does not support
- * split touch.
- * 2. Don't split mouse events
- */
-bool InputDispatcher::shouldSplitTouch(const TouchState& touchState,
- const MotionEntry& entry) const {
- if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) {
- // We should never split mouse events
- return false;
- }
- for (const TouchedWindow& touchedWindow : touchState.windows) {
- if (touchedWindow.windowHandle->getInfo()->isSpy()) {
- // Spy windows should not affect whether or not touch is split.
- continue;
- }
- if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) {
- continue;
- }
- if (touchedWindow.windowHandle->getInfo()->inputConfig.test(
- gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
- // Wallpaper window should not affect whether or not touch is split
- continue;
- }
-
- if (touchedWindow.hasTouchingPointers(entry.deviceId)) {
- return false;
- }
- }
- return true;
-}
-
std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
InputEventInjectionResult& outInjectionResult) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 512bc4f..4d145c6 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -522,7 +522,6 @@
// shade is pulled down while we are counting down the timeout).
void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
- bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) const;
int32_t getTargetDisplayId(const EventEntry& entry);
sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 31b5e28..e780a1e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1252,9 +1252,14 @@
return mBorderColor;
}
-bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded) {
- // The frame rate for layer tree is this layer's frame rate if present, or the parent frame rate
+bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
+ bool* transactionNeeded) {
+ // Gets the frame rate to propagate to children.
const auto frameRate = [&] {
+ if (overrideChildren && parentFrameRate.isValid()) {
+ return parentFrameRate;
+ }
+
if (mDrawingState.frameRate.isValid()) {
return mDrawingState.frameRate;
}
@@ -1268,7 +1273,10 @@
bool childrenHaveFrameRate = false;
for (const sp<Layer>& child : mCurrentChildren) {
childrenHaveFrameRate |=
- child->propagateFrameRateForLayerTree(frameRate, transactionNeeded);
+ child->propagateFrameRateForLayerTree(frameRate,
+ overrideChildren ||
+ shouldOverrideChildrenFrameRate(),
+ transactionNeeded);
}
// If we don't have a valid frame rate specification, but the children do, we set this
@@ -1301,7 +1309,7 @@
}();
bool transactionNeeded = false;
- root->propagateFrameRateForLayerTree({}, &transactionNeeded);
+ root->propagateFrameRateForLayerTree({}, false, &transactionNeeded);
// TODO(b/195668952): we probably don't need eTraversalNeeded here
if (transactionNeeded) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d5e2185..40882f4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1102,7 +1102,8 @@
const std::vector<Layer*>& layersInTree);
void updateTreeHasFrameRateVote();
- bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded);
+ bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
+ bool* transactionNeeded);
void setZOrderRelativeOf(const wp<Layer>& relativeOf);
bool isTrustedOverlay() const;
gui::DropInputMode getDropInputMode() const;
@@ -1164,6 +1165,11 @@
void transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles,
std::vector<JankData>& jankData);
+ bool shouldOverrideChildrenFrameRate() const {
+ return getDrawingState().frameRateSelectionStrategy ==
+ FrameRateSelectionStrategy::OverrideChildren;
+ }
+
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
// a transform from the current layer coordinate space to display(screen) coordinate space.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1a24332..ecf2310 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -7028,7 +7028,10 @@
}
case 1041: { // Transaction tracing
if (mTransactionTracing) {
- if (data.readInt32()) {
+ int arg = data.readInt32();
+ if (arg == -1) {
+ mTransactionTracing.reset();
+ } else if (arg > 0) {
// Transaction tracing is always running but allow the user to temporarily
// increase the buffer when actively debugging.
mTransactionTracing->setBufferSize(
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 8deff85..f4516c7 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -87,6 +87,7 @@
"FramebufferSurfaceTest.cpp",
"FrameRateOverrideMappingsTest.cpp",
"FrameRateSelectionPriorityTest.cpp",
+ "FrameRateSelectionStrategyTest.cpp",
"FrameTimelineTest.cpp",
"GameModeTest.cpp",
"HWComposerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
new file mode 100644
index 0000000..ffe6f24
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "Layer.h"
+#include "LayerTestUtils.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+
+namespace android {
+
+using testing::DoAll;
+using testing::Mock;
+using testing::SetArgPointee;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using scheduler::LayerHistory;
+
+using FrameRate = Layer::FrameRate;
+using FrameRateCompatibility = Layer::FrameRateCompatibility;
+using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
+
+/**
+ * This class tests the behaviour of Layer::setFrameRateSelectionStrategy.
+ */
+class FrameRateSelectionStrategyTest : public BaseLayerTest {
+protected:
+ const FrameRate FRAME_RATE_VOTE1 = FrameRate(11_Hz, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE2 = FrameRate(22_Hz, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE3 = FrameRate(33_Hz, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
+
+ FrameRateSelectionStrategyTest();
+
+ void addChild(sp<Layer> layer, sp<Layer> child);
+ void removeChild(sp<Layer> layer, sp<Layer> child);
+ void commitTransaction();
+
+ std::vector<sp<Layer>> mLayers;
+};
+
+FrameRateSelectionStrategyTest::FrameRateSelectionStrategyTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+}
+
+void FrameRateSelectionStrategyTest::addChild(sp<Layer> layer, sp<Layer> child) {
+ layer->addChild(child);
+}
+
+void FrameRateSelectionStrategyTest::removeChild(sp<Layer> layer, sp<Layer> child) {
+ layer->removeChild(child);
+}
+
+void FrameRateSelectionStrategyTest::commitTransaction() {
+ for (auto layer : mLayers) {
+ layer->commitTransaction();
+ }
+}
+
+namespace {
+
+INSTANTIATE_TEST_SUITE_P(PerLayerType, FrameRateSelectionStrategyTest,
+ testing::Values(std::make_shared<BufferStateLayerFactory>(),
+ std::make_shared<EffectLayerFactory>()),
+ PrintToStringParamName);
+
+TEST_P(FrameRateSelectionStrategyTest, SetAndGet) {
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+ const auto& layerFactory = GetParam();
+ auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ layer->setFrameRate(FRAME_RATE_VOTE1.vote);
+ layer->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ layer->getDrawingState().frameRateSelectionStrategy);
+}
+
+TEST_P(FrameRateSelectionStrategyTest, SetChildAndGetParent) {
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+ const auto& layerFactory = GetParam();
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1.vote);
+ child2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ parent->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ child1->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ child2->getDrawingState().frameRateSelectionStrategy);
+}
+
+TEST_P(FrameRateSelectionStrategyTest, SetParentAndGet) {
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+ const auto& layerFactory = GetParam();
+ auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ addChild(layer1, layer2);
+ addChild(layer2, layer3);
+
+ layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
+ layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
+ layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
+ layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
+ layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
+ layer3->setFrameRate(FRAME_RATE_VOTE3.vote);
+ commitTransaction();
+
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ layer1->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ layer2->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ layer3->getDrawingState().frameRateSelectionStrategy);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file