Merge "RESTRICT AUTOMERGE SurfaceFlinger: fix a potential race condition in stealReceiveChannel" into sc-v2-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2d11b90..30ef4b9 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1793,8 +1793,8 @@
// Add linker configuration directory
ds.AddDir(LINKERCONFIG_DIR, true);
- /* Dump cgroupfs */
- ds.AddDir(CGROUPFS_DIR, true);
+ /* Dump frozen cgroupfs */
+ dump_frozen_cgroupfs();
if (ds.dump_pool_) {
WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
@@ -4007,6 +4007,63 @@
fclose(fp);
}
+void dump_frozen_cgroupfs(const char *dir, int level,
+ int (*dump_from_fd)(const char* title, const char* path, int fd)) {
+ DIR *dirp;
+ struct dirent *d;
+ char *newpath = nullptr;
+
+ dirp = opendir(dir);
+ if (dirp == nullptr) {
+ MYLOGE("%s: %s\n", dir, strerror(errno));
+ return;
+ }
+
+ for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) {
+ if ((d->d_name[0] == '.')
+ && (((d->d_name[1] == '.') && (d->d_name[2] == '\0'))
+ || (d->d_name[1] == '\0'))) {
+ continue;
+ }
+ if (d->d_type == DT_DIR) {
+ asprintf(&newpath, "%s/%s/", dir, d->d_name);
+ if (!newpath) {
+ continue;
+ }
+ if (level == 0 && !strncmp(d->d_name, "uid_", 4)) {
+ dump_frozen_cgroupfs(newpath, 1, dump_from_fd);
+ } else if (level == 1 && !strncmp(d->d_name, "pid_", 4)) {
+ char *freezer = nullptr;
+ asprintf(&freezer, "%s/%s", newpath, "cgroup.freeze");
+ if (freezer) {
+ FILE* fp = fopen(freezer, "r");
+ if (fp != NULL) {
+ int frozen;
+ fscanf(fp, "%d", &frozen);
+ if (frozen > 0) {
+ dump_files("", newpath, skip_none, dump_from_fd);
+ }
+ fclose(fp);
+ }
+ free(freezer);
+ }
+ }
+ }
+ }
+ closedir(dirp);
+}
+
+void dump_frozen_cgroupfs() {
+ if (!ds.IsZipping()) {
+ MYLOGD("Not adding cgroupfs because it's not a zipped bugreport\n");
+ return;
+ }
+ MYLOGD("Adding frozen processes from %s\n", CGROUPFS_DIR);
+ DurationReporter duration_reporter("FROZEN CGROUPFS");
+ if (PropertiesHelper::IsDryRun()) return;
+ dump_frozen_cgroupfs(CGROUPFS_DIR, 0, _add_file_from_fd);
+}
+
void Dumpstate::UpdateProgress(int32_t delta_sec) {
if (progress_ == nullptr) {
MYLOGE("UpdateProgress: progress_ not set\n");
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 83e6787..34280d0 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -642,6 +642,9 @@
/* Prints the contents of all the routing tables, both IPv4 and IPv6. */
void dump_route_tables();
+/* Dump subdirectories of cgroupfs if the corresponding process is frozen */
+void dump_frozen_cgroupfs();
+
/* Play a sound via Stagefright */
void play_sound(const char *path);
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 0712c0a..28e5ee2 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -240,11 +240,11 @@
EXPECT_GE(st.st_size, 1000000 /* 1MB */);
}
-TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
- EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
- << duration.count() << " s.";
+TEST_F(ZippedBugreportGenerationTest, TakesBetween20And300Seconds) {
+ EXPECT_GE(duration, 20s) << "Expected completion in more than 20s. Actual time "
+ << duration.count() << " ms.";
EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
- << duration.count() << " s.";
+ << duration.count() << " ms.";
}
/**
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 95d9377..b84be9b 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -1766,6 +1766,10 @@
const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
+ if (packageNames.size() != ceDataInodes.size()) {
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+ "packageNames/ceDataInodes size mismatch.");
+ }
for (const auto& packageName : packageNames) {
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
}
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 3657497..b910ac1 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -322,6 +322,19 @@
system(removeCommand.c_str());
}
}
+TEST_F(ServiceTest, GetAppSizeWrongSizes) {
+ int32_t externalStorageAppId = -1;
+ std::vector<int64_t> externalStorageSize;
+
+ std::vector<std::string> codePaths;
+ std::vector<std::string> packageNames = {"package1", "package2"};
+ std::vector<int64_t> ceDataInodes = {0};
+
+ EXPECT_BINDER_FAIL(service->getAppSize(std::nullopt, packageNames, 0,
+ InstalldNativeService::FLAG_USE_QUOTA,
+ externalStorageAppId, ceDataInodes, codePaths,
+ &externalStorageSize));
+}
static bool mkdirs(const std::string& path, mode_t mode) {
struct stat sb;
if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 77a883b..bf275a5 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -391,6 +391,71 @@
}
}
+void layer_state_t::sanitize(int32_t permissions) {
+ // TODO: b/109894387
+ //
+ // SurfaceFlinger's renderer is not prepared to handle cropping in the face of arbitrary
+ // rotation. To see the problem observe that if we have a square parent, and a child
+ // of the same size, then we rotate the child 45 degrees around its center, the child
+ // must now be cropped to a non rectangular 8 sided region.
+ //
+ // Of course we can fix this in the future. For now, we are lucky, SurfaceControl is
+ // private API, and arbitrary rotation is used in limited use cases, for instance:
+ // - WindowManager only uses rotation in one case, which is on a top level layer in which
+ // cropping is not an issue.
+ // - Launcher, as a privileged app, uses this to transition an application to PiP
+ // (picture-in-picture) mode.
+ //
+ // However given that abuse of rotation matrices could lead to surfaces extending outside
+ // of cropped areas, we need to prevent non-root clients without permission
+ // ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER
+ // (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle
+ // preserving transformations.
+ if (what & eMatrixChanged) {
+ if (!(permissions & Permission::ROTATE_SURFACE_FLINGER)) {
+ ui::Transform t;
+ t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+ if (!t.preserveRects()) {
+ what &= ~eMatrixChanged;
+ ALOGE("Stripped non rect preserving matrix in sanitize");
+ }
+ }
+ }
+
+ if (what & layer_state_t::eInputInfoChanged) {
+ if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~eInputInfoChanged;
+ ALOGE("Stripped attempt to set eInputInfoChanged in sanitize");
+ }
+ }
+ if (what & layer_state_t::eTrustedOverlayChanged) {
+ if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~eTrustedOverlayChanged;
+ ALOGE("Stripped attempt to set eTrustedOverlay in sanitize");
+ }
+ }
+ if (what & layer_state_t::eDropInputModeChanged) {
+ if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~eDropInputModeChanged;
+ ALOGE("Stripped attempt to set eDropInputModeChanged in sanitize");
+ }
+ }
+ if (what & layer_state_t::eFrameRateSelectionPriority) {
+ if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~eFrameRateSelectionPriority;
+ ALOGE("Stripped attempt to set eFrameRateSelectionPriority in sanitize");
+ }
+ }
+ if (what & layer_state_t::eFrameRateChanged) {
+ if (!ValidateFrameRate(frameRate, frameRateCompatibility,
+ changeFrameRateStrategy,
+ "layer_state_t::sanitize",
+ permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~eFrameRateChanged; // logged in ValidateFrameRate
+ }
+ }
+}
+
void layer_state_t::merge(const layer_state_t& other) {
if (other.what & ePositionChanged) {
what |= ePositionChanged;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 725ea65..4bcc9d5 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -551,6 +551,13 @@
mListenerCallbacks = other.mListenerCallbacks;
}
+void SurfaceComposerClient::Transaction::sanitize() {
+ for (auto & [handle, composerState] : mComposerStates) {
+ composerState.state.sanitize(0 /* permissionMask */);
+ }
+ mInputWindowCommands.clear();
+}
+
std::unique_ptr<SurfaceComposerClient::Transaction>
SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) {
auto transaction = std::make_unique<Transaction>();
@@ -635,7 +642,6 @@
if (composerState.read(*parcel) == BAD_VALUE) {
return BAD_VALUE;
}
-
composerStates[surfaceControlHandle] = composerState;
}
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 6529a4e..f93b799 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -70,6 +70,7 @@
mLayerId = other->mLayerId;
mWidth = other->mWidth;
mHeight = other->mHeight;
+ mFormat = other->mFormat;
mCreateFlags = other->mCreateFlags;
}
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 03e4aac..2a8d30d 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -61,6 +61,12 @@
* Used to communicate layer information between SurfaceFlinger and its clients.
*/
struct layer_state_t {
+ enum Permission {
+ ACCESS_SURFACE_FLINGER = 0x1,
+ ROTATE_SURFACE_FLINGER = 0x2,
+ INTERNAL_SYSTEM_WINDOW = 0x4,
+ };
+
enum {
eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
eLayerOpaque = 0x02, // SURFACE_OPAQUE
@@ -128,6 +134,7 @@
status_t read(const Parcel& input);
bool hasBufferChanges() const;
bool hasValidBuffer() const;
+ void sanitize(int32_t permissions);
struct matrix22_t {
float dsdx{0};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0d1d1a3..76b6d44 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -590,6 +590,14 @@
void setAnimationTransaction();
void setEarlyWakeupStart();
void setEarlyWakeupEnd();
+
+ /**
+ * Strip the transaction of all permissioned requests, required when
+ * accepting transactions across process boundaries.
+ *
+ * TODO (b/213644870): Remove all permissioned things from Transaction
+ */
+ void sanitize();
};
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 9ee4636..ed14ec6 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -118,12 +118,13 @@
mutable sp<Surface> mSurfaceData;
mutable sp<BLASTBufferQueue> mBbq;
mutable sp<SurfaceControl> mBbqChild;
- int32_t mLayerId;
- uint32_t mTransformHint;
- uint32_t mWidth;
- uint32_t mHeight;
- PixelFormat mFormat;
- uint32_t mCreateFlags;
+
+ int32_t mLayerId = 0;
+ uint32_t mTransformHint = 0;
+ uint32_t mWidth = 0;
+ uint32_t mHeight = 0;
+ PixelFormat mFormat = PIXEL_FORMAT_NONE;
+ uint32_t mCreateFlags = 0;
};
}; // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 0ee9fb3..a56827e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -134,6 +134,68 @@
return true;
}
+float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(
+ const LayerRequirement& layer, const RefreshRate& refreshRate) const {
+ constexpr float kScoreForFractionalPairs = .8f;
+
+ const auto displayPeriod = refreshRate.getVsyncPeriod();
+ 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 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;
+ }
+
+ // Calculate how many display vsyncs we need to present a single frame for this
+ // layer
+ const auto [displayFramesQuotient, displayFramesRemainder] =
+ getDisplayFrames(layerPeriod, displayPeriod);
+ static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+ if (displayFramesRemainder == 0) {
+ // Layer desired refresh rate matches the display rate.
+ return 1.0f;
+ }
+
+ if (displayFramesQuotient == 0) {
+ // Layer desired refresh rate is higher than the display rate.
+ return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+ (1.0f / (MAX_FRAMES_TO_FIT + 1));
+ }
+
+ // Layer desired refresh rate is lower than the display rate. Check how well it fits
+ // the cadence.
+ auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
+ int iter = 2;
+ while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+ diff = diff - (displayPeriod - diff);
+ iter++;
+ }
+
+ return (1.0f / iter);
+ }
+
+ return 0;
+}
+
float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
const RefreshRate& refreshRate,
bool isSeamlessSwitch) const {
@@ -153,51 +215,6 @@
return ratio * ratio;
}
- const auto displayPeriod = refreshRate.getVsyncPeriod();
- 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
- auto actualLayerPeriod = displayPeriod;
- int multiplier = 1;
- while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
- multiplier++;
- actualLayerPeriod = displayPeriod * multiplier;
- }
- return std::min(1.0f,
- static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
- }
-
- if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
- layer.vote == LayerVoteType::Heuristic) {
- // Calculate how many display vsyncs we need to present a single frame for this
- // layer
- const auto [displayFramesQuotient, displayFramesRemainder] =
- getDisplayFrames(layerPeriod, displayPeriod);
- static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
- if (displayFramesRemainder == 0) {
- // Layer desired refresh rate matches the display rate.
- return 1.0f * seamlessness;
- }
-
- if (displayFramesQuotient == 0) {
- // Layer desired refresh rate is higher than the display rate.
- return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
- (1.0f / (MAX_FRAMES_TO_FIT + 1));
- }
-
- // Layer desired refresh rate is lower than the display rate. Check how well it fits
- // the cadence.
- auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
- int iter = 2;
- while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
- diff = diff - (displayPeriod - diff);
- iter++;
- }
-
- return (1.0f / iter) * seamlessness;
- }
-
if (layer.vote == LayerVoteType::ExplicitExact) {
const int divider = getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate);
if (mSupportsFrameRateOverride) {
@@ -210,7 +227,18 @@
return divider == 1;
}
- return 0;
+ // If the layer frame rate is a divider of the refresh rate it should score
+ // the highest score.
+ if (getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
+ return 1.0f * seamlessness;
+ }
+
+ // The layer frame rate is not a divider of the refresh rate,
+ // there is a small penalty attached to the score to favor the frame rates
+ // the exactly matches the display refresh rate or a multiple.
+ constexpr float kNonExactMatchingPenalty = 0.95f;
+ return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
+ kNonExactMatchingPenalty;
}
struct RefreshRateScore {
@@ -422,7 +450,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;
}
@@ -583,7 +611,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) {
@@ -592,7 +620,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;
}
@@ -945,6 +973,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 21867cc..99f217c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -344,6 +344,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.
//
@@ -446,6 +450,9 @@
float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
bool isSeamlessSwitch) const REQUIRES(mLock);
+ float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&,
+ const RefreshRate&) const REQUIRES(mLock);
+
void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
void initializeIdleTimer();
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index c1dba2b..77b75ab 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),
@@ -158,7 +175,6 @@
RefreshRate mExpected30Config = {mConfig30, RefreshRate::ConstructorTag(0)};
RefreshRate mExpected120Config = {mConfig120, RefreshRate::ConstructorTag(0)};
-private:
DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
ui::Size resolution = ui::Size());
};
@@ -1230,7 +1246,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 +2146,100 @@
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}));
+}
+
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) {
+ constexpr int kMinRefreshRate = 10;
+ constexpr int kMaxRefreshRate = 240;
+
+ DisplayModes displayModes;
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ constexpr int32_t kGroup = 0;
+ const auto refreshRate = Fps(static_cast<float>(fps));
+ displayModes.push_back(
+ createDisplayMode(DisplayModeId(fps), kGroup, refreshRate.getPeriodNsecs()));
+ }
+
+ const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(displayModes,
+ /*currentConfigId=*/displayModes[0]->getId());
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
+ layers[0].desiredRefreshRate = fps;
+ layers[0].vote = vote;
+ EXPECT_EQ(fps.getIntValue(),
+ refreshRateConfigs->getBestRefreshRate(layers, globalSignals)
+ .getFps()
+ .getIntValue())
+ << "Failed for " << RefreshRateConfigs::layerVoteTypeString(vote);
+ };
+
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ const auto refreshRate = Fps(static_cast<float>(fps));
+ testRefreshRate(refreshRate, LayerVoteType::Heuristic);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
+ }
+}
+
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
+ const DisplayModes displayModes = {
+ createDisplayMode(DisplayModeId(0), 0, Fps(43.0f).getPeriodNsecs()),
+ createDisplayMode(DisplayModeId(1), 0, Fps(53.0f).getPeriodNsecs()),
+ createDisplayMode(DisplayModeId(2), 0, Fps(55.0f).getPeriodNsecs()),
+ createDisplayMode(DisplayModeId(3), 0, Fps(60.0f).getPeriodNsecs()),
+ };
+
+ const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(displayModes,
+ /*currentConfigId=*/displayModes[0]->getId());
+
+ const auto layers = std::vector<LayerRequirement>{
+ LayerRequirement{
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = Fps(43.0f),
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.41f,
+ },
+ LayerRequirement{
+ .vote = LayerVoteType::ExplicitExactOrMultiple,
+ .desiredRefreshRate = Fps(53.0f),
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.41f,
+ },
+ };
+
+ EXPECT_EQ(53,
+ refreshRateConfigs->getBestRefreshRate(layers, globalSignals).getFps().getIntValue());
+}
+
TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
EXPECT_TRUE(mExpected60Config < mExpected90Config);
EXPECT_FALSE(mExpected60Config < mExpected60Config);
@@ -2123,6 +2335,33 @@
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) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/