Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8532956a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "SF: Remove EventControlThread"

parents 81ec695b 8b01cc0b
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -162,7 +162,6 @@ filegroup {
        "RenderArea.cpp",
        "Scheduler/DispSync.cpp",
        "Scheduler/DispSyncSource.cpp",
        "Scheduler/EventControlThread.cpp",
        "Scheduler/EventThread.cpp",
        "Scheduler/OneShotTimer.cpp",
        "Scheduler/LayerHistory.cpp",
+21 −25
Original line number Diff line number Diff line
@@ -14,8 +14,7 @@
 * limitations under the License.
 */

#ifndef ANDROID_SF_HWC2_H
#define ANDROID_SF_HWC2_H
#pragma once

#include <gui/HdrMetadata.h>
#include <math/mat4.h>
@@ -36,15 +35,16 @@
#include "Hal.h"

namespace android {
    struct DisplayedFrameStats;

class Fence;
class FloatRect;
class GraphicBuffer;
class TestableSurfaceFlinger;
struct DisplayedFrameStats;

namespace Hwc2 {
class Composer;
    }

    class TestableSurfaceFlinger;
} // namespace Hwc2

namespace HWC2 {

@@ -61,19 +61,17 @@ namespace hal = android::hardware::graphics::composer::hal;
// All calls receive a sequenceId, which will be the value that was supplied to
// HWC2::Device::registerCallback(). It's used to help differentiate callbacks
// from different hardware composer instances.
class ComposerCallback {
 public:
     virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId display,
                                    hal::Connection connection) = 0;
     virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId display) = 0;
     virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp,
                                  std::optional<hal::VsyncPeriodNanos> vsyncPeriod) = 0;
     virtual void onVsyncPeriodTimingChangedReceived(
             int32_t sequenceId, hal::HWDisplayId display,
             const hal::VsyncPeriodChangeTimeline& updatedTimeline) = 0;
     virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) = 0;

     virtual ~ComposerCallback() = default;
struct ComposerCallback {
    virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId, hal::Connection) = 0;
    virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId) = 0;
    virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp,
                                 std::optional<hal::VsyncPeriodNanos>) = 0;
    virtual void onVsyncPeriodTimingChangedReceived(int32_t sequenceId, hal::HWDisplayId,
                                                    const hal::VsyncPeriodChangeTimeline&) = 0;
    virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId) = 0;

protected:
    ~ComposerCallback() = default;
};

// Convenience C++ class to access per display functions directly.
@@ -454,5 +452,3 @@ private:
} // namespace impl
} // namespace HWC2
} // namespace android

#endif // ANDROID_SF_HWC2_H
+0 −82
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 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.
 */

// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"

#include <pthread.h>
#include <sched.h>
#include <sys/resource.h>

#include <cutils/sched_policy.h>
#include <log/log.h>
#include <system/thread_defs.h>

#include "EventControlThread.h"

namespace android {

EventControlThread::~EventControlThread() = default;

namespace impl {

EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
      : mSetVSyncEnabled(std::move(function)) {
    pthread_setname_np(mThread.native_handle(), "EventControlThread");

    pid_t tid = pthread_gettid_np(mThread.native_handle());
    setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY);
    set_sched_policy(tid, SP_FOREGROUND);
}

EventControlThread::~EventControlThread() {
    {
        std::lock_guard<std::mutex> lock(mMutex);
        mKeepRunning = false;
        mCondition.notify_all();
    }
    mThread.join();
}

void EventControlThread::setVsyncEnabled(bool enabled) {
    std::lock_guard<std::mutex> lock(mMutex);
    mVsyncEnabled = enabled;
    mCondition.notify_all();
}

// Unfortunately std::unique_lock gives warnings with -Wthread-safety
void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
    auto keepRunning = true;
    auto currentVsyncEnabled = false;

    while (keepRunning) {
        mSetVSyncEnabled(currentVsyncEnabled);

        std::unique_lock<std::mutex> lock(mMutex);
        mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS {
            return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning;
        });
        currentVsyncEnabled = mVsyncEnabled;
        keepRunning = mKeepRunning;
    }
}

} // namespace impl
} // namespace android

// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+0 −63
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <condition_variable>
#include <cstddef>
#include <functional>
#include <mutex>
#include <thread>

#include <android-base/thread_annotations.h>

namespace android {

class EventControlThread {
public:
    virtual ~EventControlThread();

    virtual void setVsyncEnabled(bool enabled) = 0;
};

namespace impl {

class EventControlThread final : public android::EventControlThread {
public:
    using SetVSyncEnabledFunction = std::function<void(bool)>;

    explicit EventControlThread(SetVSyncEnabledFunction function);
    ~EventControlThread();

    // EventControlThread implementation
    void setVsyncEnabled(bool enabled) override;

private:
    void threadMain();

    std::mutex mMutex;
    std::condition_variable mCondition;

    const SetVSyncEnabledFunction mSetVSyncEnabled;
    bool mVsyncEnabled GUARDED_BY(mMutex) = false;
    bool mKeepRunning GUARDED_BY(mMutex) = true;

    // Must be last so that everything is initialized before the thread starts.
    std::thread mThread{&EventControlThread::threadMain, this};
};

} // namespace impl
} // namespace android
+84 −92
Original line number Diff line number Diff line
@@ -20,11 +20,11 @@

#include "Scheduler.h"

#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
#include <cutils/properties.h>
#include <input/InputWindow.h>
#include <system/window.h>
#include <ui/DisplayStatInfo.h>
@@ -41,7 +41,6 @@
#include "../Layer.h"
#include "DispSync.h"
#include "DispSyncSource.h"
#include "EventControlThread.h"
#include "EventThread.h"
#include "InjectVSyncSource.h"
#include "OneShotTimer.h"
@@ -60,54 +59,35 @@
        }                                                            \
    } while (false)

using namespace std::string_literals;

namespace android {

namespace {

std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() {
    // TODO (144707443) tune Predictor tunables.
    static constexpr int default_rate = 60;
    static constexpr auto initial_period =
            std::chrono::duration<nsecs_t, std::ratio<1, default_rate>>(1);
    static constexpr size_t vsyncTimestampHistorySize = 20;
    static constexpr size_t minimumSamplesForPrediction = 6;
    static constexpr uint32_t discardOutlierPercent = 20;
    return std::make_unique<
            scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>(
                                               initial_period)
                                               .count(),
                                       vsyncTimestampHistorySize, minimumSamplesForPrediction,
    // TODO(b/144707443): Tune constants.
    constexpr int kDefaultRate = 60;
    constexpr auto initialPeriod = std::chrono::duration<nsecs_t, std::ratio<1, kDefaultRate>>(1);
    constexpr nsecs_t idealPeriod =
            std::chrono::duration_cast<std::chrono::nanoseconds>(initialPeriod).count();
    constexpr size_t vsyncTimestampHistorySize = 20;
    constexpr size_t minimumSamplesForPrediction = 6;
    constexpr uint32_t discardOutlierPercent = 20;
    return std::make_unique<scheduler::VSyncPredictor>(idealPeriod, vsyncTimestampHistorySize,
                                                       minimumSamplesForPrediction,
                                                       discardOutlierPercent);
}

std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(
        const std::unique_ptr<scheduler::VSyncTracker>& vSyncTracker) {
    if (!vSyncTracker) return {};

    // TODO (144707443) tune Predictor tunables.
    static constexpr auto vsyncMoveThreshold =
            std::chrono::duration_cast<std::chrono::nanoseconds>(3ms);
    static constexpr auto timerSlack = std::chrono::duration_cast<std::chrono::nanoseconds>(500us);
std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(scheduler::VSyncTracker& tracker) {
    // TODO(b/144707443): Tune constants.
    constexpr std::chrono::nanoseconds vsyncMoveThreshold = 3ms;
    constexpr std::chrono::nanoseconds timerSlack = 500us;
    return std::make_unique<
            scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *vSyncTracker,
            scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), tracker,
                                                timerSlack.count(), vsyncMoveThreshold.count());
}

std::unique_ptr<DispSync> createDispSync(
        const std::unique_ptr<scheduler::VSyncTracker>& vSyncTracker,
        const std::unique_ptr<scheduler::VSyncDispatch>& vSyncDispatch, bool supportKernelTimer) {
    if (vSyncTracker && vSyncDispatch) {
        // TODO (144707443) tune Predictor tunables.
        static constexpr size_t pendingFenceLimit = 20;
        return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
                                                         *vSyncDispatch, *vSyncTracker,
                                                         pendingFenceLimit, supportKernelTimer);
    } else {
        return std::make_unique<impl::DispSync>("SchedulerDispSync",
                                                sysprop::running_without_sync_framework(true));
    }
}

const char* toContentDetectionString(bool useContentDetection, bool useContentDetectionV2) {
    if (!useContentDetection) return "off";
    return useContentDetectionV2 ? "V2" : "V1";
@@ -115,28 +95,25 @@ const char* toContentDetectionString(bool useContentDetection, bool useContentDe

} // namespace

Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
                     const scheduler::RefreshRateConfigs& refreshRateConfigs,
                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
                     bool useContentDetection)
      : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
        // TODO (140302863) remove this and use the vsync_reactor system.
        mUseVsyncPredictor(property_get_bool("debug.sf.vsync_reactor", true)),
        mVSyncTracker(mUseVsyncPredictor ? createVSyncTracker() : nullptr),
        mVSyncDispatch(createVSyncDispatch(mVSyncTracker)),
        mPrimaryDispSync(createDispSync(mVSyncTracker, mVSyncDispatch, mSupportKernelTimer)),
        mEventControlThread(new impl::EventControlThread(std::move(function))),
        mLayerHistory(createLayerHistory(refreshRateConfigs, useContentDetectionV2)),
        mSchedulerCallback(schedulerCallback),
        mRefreshRateConfigs(refreshRateConfigs),
        mUseContentDetection(useContentDetection),
        mUseContentDetectionV2(useContentDetectionV2) {
Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
      : Scheduler(configs, callback,
                  {.supportKernelTimer = sysprop::support_kernel_idle_timer(false),
                   .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false),
                   .useContentDetectionV2 =
                           base::GetBoolProperty("debug.sf.use_content_detection_v2"s, true),
                   // TODO(b/140302863): Remove this and use the vsync_reactor system.
                   .useVsyncPredictor = base::GetBoolProperty("debug.sf.vsync_reactor"s, true)}) {}

Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
                     Options options)
      : Scheduler(createVsyncSchedule(options), configs, callback,
                  createLayerHistory(configs, options.useContentDetectionV2), options) {
    using namespace sysprop;

    const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
    const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);

    if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
        const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
        const auto callback = mOptions.supportKernelTimer ? &Scheduler::kernelIdleTimerCallback
                                                          : &Scheduler::idleTimerCallback;
        mIdleTimer.emplace(
                std::chrono::milliseconds(millis),
@@ -163,21 +140,16 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
    }
}

Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
                     std::unique_ptr<EventControlThread> eventControlThread,
                     const scheduler::RefreshRateConfigs& configs,
Scheduler::Scheduler(VsyncSchedule schedule, const scheduler::RefreshRateConfigs& configs,
                     ISchedulerCallback& schedulerCallback,
                     std::unique_ptr<LayerHistory> layerHistory, bool useContentDetectionV2,
                     bool useContentDetection)
      : mSupportKernelTimer(false),
        mUseVsyncPredictor(true),
        mPrimaryDispSync(std::move(primaryDispSync)),
        mEventControlThread(std::move(eventControlThread)),
                     std::unique_ptr<LayerHistory> layerHistory, Options options)
      : mOptions(options),
        mVsyncSchedule(std::move(schedule)),
        mLayerHistory(std::move(layerHistory)),
        mSchedulerCallback(schedulerCallback),
        mRefreshRateConfigs(configs),
        mUseContentDetection(useContentDetection),
        mUseContentDetectionV2(useContentDetectionV2) {}
        mRefreshRateConfigs(configs) {
    mSchedulerCallback.setVsyncEnabled(false);
}

Scheduler::~Scheduler() {
    // Ensure the OneShotTimer threads are joined before we start destroying state.
@@ -186,6 +158,25 @@ Scheduler::~Scheduler() {
    mIdleTimer.reset();
}

Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(Options options) {
    if (!options.useVsyncPredictor) {
        auto sync = std::make_unique<impl::DispSync>("SchedulerDispSync",
                                                     sysprop::running_without_sync_framework(true));
        return {std::move(sync), nullptr, nullptr};
    }

    auto clock = std::make_unique<scheduler::SystemClock>();
    auto tracker = createVSyncTracker();
    auto dispatch = createVSyncDispatch(*tracker);

    // TODO(b/144707443): Tune constants.
    constexpr size_t pendingFenceLimit = 20;
    auto sync = std::make_unique<scheduler::VSyncReactor>(std::move(clock), *dispatch, *tracker,
                                                          pendingFenceLimit,
                                                          options.supportKernelTimer);
    return {std::move(sync), std::move(tracker), std::move(dispatch)};
}

std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
        const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) {
    if (!configs.canSwitch()) return nullptr;
@@ -198,12 +189,12 @@ std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
}

DispSync& Scheduler::getPrimaryDispSync() {
    return *mPrimaryDispSync;
    return *mVsyncSchedule.sync;
}

std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
                                                                  nsecs_t phaseOffsetNs) {
    return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
    return std::make_unique<DispSyncSource>(&getPrimaryDispSync(), phaseOffsetNs,
                                            true /* traceVsync */, name);
}

@@ -309,8 +300,8 @@ void Scheduler::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
}

void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
    stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0, systemTime());
    stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
    stats->vsyncTime = getPrimaryDispSync().computeNextRefresh(0, systemTime());
    stats->vsyncPeriod = getPrimaryDispSync().getPeriod();
}

Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
@@ -347,8 +338,8 @@ bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime) {
void Scheduler::enableHardwareVsync() {
    std::lock_guard<std::mutex> lock(mHWVsyncLock);
    if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
        mPrimaryDispSync->beginResync();
        mEventControlThread->setVsyncEnabled(true);
        getPrimaryDispSync().beginResync();
        mSchedulerCallback.setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }
}
@@ -356,8 +347,8 @@ void Scheduler::enableHardwareVsync() {
void Scheduler::disableHardwareVsync(bool makeUnavailable) {
    std::lock_guard<std::mutex> lock(mHWVsyncLock);
    if (mPrimaryHWVsyncEnabled) {
        mEventControlThread->setVsyncEnabled(false);
        mPrimaryDispSync->endResync();
        mSchedulerCallback.setVsyncEnabled(false);
        getPrimaryDispSync().endResync();
        mPrimaryHWVsyncEnabled = false;
    }
    if (makeUnavailable) {
@@ -397,11 +388,11 @@ void Scheduler::resync() {

void Scheduler::setVsyncPeriod(nsecs_t period) {
    std::lock_guard<std::mutex> lock(mHWVsyncLock);
    mPrimaryDispSync->setPeriod(period);
    getPrimaryDispSync().setPeriod(period);

    if (!mPrimaryHWVsyncEnabled) {
        mPrimaryDispSync->beginResync();
        mEventControlThread->setVsyncEnabled(true);
        getPrimaryDispSync().beginResync();
        mSchedulerCallback.setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }
}
@@ -414,7 +405,7 @@ void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsy
        std::lock_guard<std::mutex> lock(mHWVsyncLock);
        if (mPrimaryHWVsyncEnabled) {
            needsHwVsync =
                    mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
                    getPrimaryDispSync().addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
        }
    }

@@ -426,7 +417,7 @@ void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsy
}

void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
    if (mPrimaryDispSync->addPresentFence(fenceTime)) {
    if (getPrimaryDispSync().addPresentFence(fenceTime)) {
        enableHardwareVsync();
    } else {
        disableHardwareVsync(false);
@@ -434,11 +425,11 @@ void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
}

void Scheduler::setIgnorePresentFences(bool ignore) {
    mPrimaryDispSync->setIgnorePresentFences(ignore);
    getPrimaryDispSync().setIgnorePresentFences(ignore);
}

nsecs_t Scheduler::getDispSyncExpectedPresentTime(nsecs_t now) {
    return mPrimaryDispSync->expectedPresentTime(now);
    return getPrimaryDispSync().expectedPresentTime(now);
}

void Scheduler::registerLayer(Layer* layer) {
@@ -450,13 +441,13 @@ void Scheduler::registerLayer(Layer* layer) {
    if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
        mLayerHistory->registerLayer(layer, minFps, maxFps,
                                     scheduler::LayerHistory::LayerVoteType::NoVote);
    } else if (!mUseContentDetection) {
    } else if (!mOptions.useContentDetection) {
        // If the content detection feature is off, all layers are registered at Max. We still keep
        // the layer history, since we use it for other features (like Frame Rate API), so layers
        // still need to be registered.
        mLayerHistory->registerLayer(layer, minFps, maxFps,
                                     scheduler::LayerHistory::LayerVoteType::Max);
    } else if (!mUseContentDetectionV2) {
    } else if (!mOptions.useContentDetectionV2) {
        // In V1 of content detection, all layers are registered as Heuristic (unless it's
        // wallpaper).
        const auto highFps =
@@ -533,7 +524,7 @@ void Scheduler::notifyTouchEvent() {
    if (mTouchTimer) {
        mTouchTimer->reset();

        if (mSupportKernelTimer && mIdleTimer) {
        if (mOptions.supportKernelTimer && mIdleTimer) {
            mIdleTimer->reset();
        }
    }
@@ -610,7 +601,8 @@ void Scheduler::dump(std::string& result) const {
    StringAppendF(&result, "+  Touch timer: %s\n",
                  mTouchTimer ? mTouchTimer->dump().c_str() : "off");
    StringAppendF(&result, "+  Content detection: %s %s\n\n",
                  toContentDetectionString(mUseContentDetection, mUseContentDetectionV2),
                  toContentDetectionString(mOptions.useContentDetection,
                                           mOptions.useContentDetectionV2),
                  mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
}

@@ -658,7 +650,7 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(
    const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
    const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired;

    if (!mUseContentDetectionV2) {
    if (!mOptions.useContentDetectionV2) {
        // As long as touch is active we want to be in performance mode.
        if (touchActive) {
            return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
Loading