diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-17 21:05:31 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-17 21:05:31 +0000 |
commit | 8ad1f63e6209f7b58ccf38fc35fb457e06625d4d (patch) | |
tree | 7eb986f5a24d492374f09523eaee40811ef38f2f | |
parent | 89c808424fbce9e40c0d4e0d1920b3c64a191b7f (diff) | |
parent | dccd7d4c75f3a988d2e66b84dd0eb247a8df9e95 (diff) | |
download | native-8ad1f63e6209f7b58ccf38fc35fb457e06625d4d.tar.gz |
Snap for 8589293 from dccd7d4c75f3a988d2e66b84dd0eb247a8df9e95 to sc-v2-platform-releaseandroid-platform-12.1.0_r5android-platform-12.1.0_r4
Change-Id: I04b77150dbff74ce1a1ec675c0f5c844d33a62fe
-rw-r--r-- | cmds/dumpstate/dumpstate.cpp | 61 | ||||
-rw-r--r-- | cmds/dumpstate/dumpstate.h | 3 | ||||
-rw-r--r-- | cmds/dumpstate/tests/dumpstate_smoke_test.cpp | 8 | ||||
-rw-r--r-- | cmds/installd/InstalldNativeService.cpp | 4 | ||||
-rw-r--r-- | cmds/installd/tests/installd_service_test.cpp | 13 | ||||
-rw-r--r-- | libs/gui/LayerState.cpp | 65 | ||||
-rw-r--r-- | libs/gui/SurfaceComposerClient.cpp | 8 | ||||
-rw-r--r-- | libs/gui/include/gui/LayerState.h | 7 | ||||
-rw-r--r-- | libs/gui/include/gui/SurfaceComposerClient.h | 8 | ||||
-rw-r--r-- | services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp | 89 | ||||
-rw-r--r-- | services/surfaceflinger/Scheduler/RefreshRateConfigs.h | 7 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp | 243 |
12 files changed, 482 insertions, 34 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 2d11b908c2..30ef4b9f55 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1793,8 +1793,8 @@ static Dumpstate::RunStatus dumpstate() { // 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 @@ void dump_route_tables() { 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 83e6787ebf..34280d01e8 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -642,6 +642,9 @@ void do_dmesg(); /* 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 0712c0adcd..28e5ee2ca9 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -240,11 +240,11 @@ TEST_F(ZippedBugreportGenerationTest, Is1MBMBinSize) { 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 95d9377a89..b84be9b1e6 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -1766,6 +1766,10 @@ binder::Status InstalldNativeService::getAppSize(const std::optional<std::string 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 36574974d9..b910ac103b 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -322,6 +322,19 @@ TEST_F(ServiceTest, GetAppSize) { 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 77a883b332..bf275a5900 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -391,6 +391,71 @@ void DisplayState::merge(const DisplayState& other) { } } +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 725ea6571d..4bcc9d56c8 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -551,6 +551,13 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) 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 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel if (composerState.read(*parcel) == BAD_VALUE) { return BAD_VALUE; } - composerStates[surfaceControlHandle] = composerState; } diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 03e4aacdbe..2a8d30d2da 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -61,6 +61,12 @@ struct client_cache_t { * 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 @@ struct layer_state_t { 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 0d1d1a37bd..76b6d44fd2 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -590,6 +590,14 @@ public: 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/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 0ee9fb353d..a56827e8f1 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -134,42 +134,37 @@ bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, return true; } -float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, - const RefreshRate& refreshRate, - bool isSeamlessSwitch) const { - if (!isVoteAllowed(layer, refreshRate)) { - return 0; - } - - // Slightly prefer seamless switches. - constexpr float kSeamedSwitchPenalty = 0.95f; - const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; - - // If the layer wants Max, give higher score to the higher refresh rate - if (layer.vote == LayerVoteType::Max) { - const auto ratio = refreshRate.getFps().getValue() / - mAppRequestRefreshRates.back()->getFps().getValue(); - // use ratio^2 to get a lower score the more we get further from peak - return ratio * ratio; - } +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 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; + } + // Calculate how many display vsyncs we need to present a single frame for this // layer const auto [displayFramesQuotient, displayFramesRemainder] = @@ -177,7 +172,7 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye 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; + return 1.0f; } if (displayFramesQuotient == 0) { @@ -195,7 +190,29 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye iter++; } - return (1.0f / iter) * seamlessness; + return (1.0f / iter); + } + + return 0; +} + +float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, + const RefreshRate& refreshRate, + bool isSeamlessSwitch) const { + if (!isVoteAllowed(layer, refreshRate)) { + return 0; + } + + // Slightly prefer seamless switches. + constexpr float kSeamedSwitchPenalty = 0.95f; + const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; + + // If the layer wants Max, give higher score to the higher refresh rate + if (layer.vote == LayerVoteType::Max) { + const auto ratio = refreshRate.getFps().getValue() / + mAppRequestRefreshRates.back()->getFps().getValue(); + // use ratio^2 to get a lower score the more we get further from peak + return ratio * ratio; } if (layer.vote == LayerVoteType::ExplicitExact) { @@ -210,7 +227,18 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye 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 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( 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 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr 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 @@ const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) 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 @@ int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrame 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 21867ccac9..99f217c2e2 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -344,6 +344,10 @@ public: // 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 @@ private: 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 c1dba2bd61..77b75ab767 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -98,9 +98,15 @@ protected: 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 @@ protected: 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 @@ protected: 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 @@ protected: 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 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) { 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 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) { 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 @@ TEST_F(RefreshRateConfigsTest, getFrameRateDivider) { 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=*/ |