Merge "Respect buffer source crop in client composition."
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index a2b4da2..cf3b172 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -154,9 +154,7 @@
 
     // fallback to original method
     if (result != 0) {
-#ifndef VENDORSERVICEMANAGER
         android_errorWriteLog(0x534e4554, "121035042");
-#endif
 
         result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
     }
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index ca004e9..ec3fac5 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -77,11 +77,9 @@
     ad.uid = uid;
     ad.name = name;
 
-#ifndef VENDORSERVICEMANAGER
     if (sid == NULL) {
         android_errorWriteLog(0x534e4554, "121035042");
     }
-#endif
 
     int result = selinux_check_access(sid ? sid : lookup_sid, tctx, class, perm, (void *) &ad);
     allowed = (result == 0);
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 72b7969..9af189a 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -3131,7 +3131,9 @@
 
             for (size_t i = 0; i < newHandles.size(); i++) {
                 const sp<InputWindowHandle>& windowHandle = newHandles.itemAt(i);
-                if (windowHandle->getInfo()->hasFocus && windowHandle->getInfo()->visible) {
+                // Set newFocusedWindowHandle to the top most focused window instead of the last one
+                if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus
+                        && windowHandle->getInfo()->visible) {
                     newFocusedWindowHandle = windowHandle;
                 }
                 if (windowHandle == mLastHoverWindowHandle) {
@@ -3175,7 +3177,7 @@
             }
 
             if (mFocusedDisplayId == displayId) {
-                onFocusChangedLocked(newFocusedWindowHandle);
+                onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
             }
 
         }
@@ -3293,7 +3295,7 @@
             // Sanity check
             sp<InputWindowHandle> newFocusedWindowHandle =
                     getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
-            onFocusChangedLocked(newFocusedWindowHandle);
+            onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
 
             if (newFocusedWindowHandle == nullptr) {
                 ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
@@ -3855,11 +3857,14 @@
     commandEntry->connection = connection;
 }
 
-void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& newFocus) {
-    sp<IBinder> token = newFocus != nullptr ? newFocus->getToken() : nullptr;
+void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
+        const sp<InputWindowHandle>& newFocus) {
+    sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
+    sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
     CommandEntry* commandEntry = postCommandLocked(
             & InputDispatcher::doNotifyFocusChangedLockedInterruptible);
-    commandEntry->token = token;
+    commandEntry->oldToken = oldToken;
+    commandEntry->newToken = newToken;
 }
 
 void InputDispatcher::onANRLocked(
@@ -3921,9 +3926,10 @@
 
 void InputDispatcher::doNotifyFocusChangedLockedInterruptible(
         CommandEntry* commandEntry) {
-    sp<IBinder> token = commandEntry->token;
+    sp<IBinder> oldToken = commandEntry->oldToken;
+    sp<IBinder> newToken = commandEntry->newToken;
     mLock.unlock();
-    mPolicy->notifyFocusChanged(token);
+    mPolicy->notifyFocusChanged(oldToken, newToken);
     mLock.lock();
 }
 
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 00390a7..49de6f3 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -217,7 +217,7 @@
 
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
-    virtual void notifyFocusChanged(const sp<IBinder>& token) = 0;
+    virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
 
     /* Gets the input dispatcher configuration. */
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
@@ -628,7 +628,8 @@
         uint32_t seq;
         bool handled;
         sp<InputChannel> inputChannel;
-        sp<IBinder> token;
+        sp<IBinder> oldToken;
+        sp<IBinder> newToken;
     };
 
     // Generic queue implementation.
@@ -1158,7 +1159,8 @@
             nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
     void onDispatchCycleBrokenLocked(
             nsecs_t currentTime, const sp<Connection>& connection);
-    void onFocusChangedLocked(const sp<InputWindowHandle>& newFocus);
+    void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
+            const sp<InputWindowHandle>& newFocus);
     void onANRLocked(
             nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
             const sp<InputWindowHandle>& windowHandle,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 704c13a..3edec40 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -63,7 +63,7 @@
     virtual void notifyInputChannelBroken(const sp<IBinder>&) {
     }
 
-    virtual void notifyFocusChanged(const sp<IBinder>&) {
+    virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) {
     }
 
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
@@ -515,7 +515,7 @@
     sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
             ADISPLAY_ID_DEFAULT);
 
-    // Set focus application.
+    // Set focused application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
     // Expect one focus window exist in display.
@@ -533,6 +533,32 @@
     windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
 }
 
+TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+            ADISPLAY_ID_DEFAULT);
+
+    // Set focused application.
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
+    windowTop->setFocus();
+    windowSecond->setFocus();
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(windowTop);
+    inputWindowHandles.add(windowSecond);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Top focused window should receive event.
+    windowTop->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowSecond->assertNoEvents();
+}
+
 TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) {
     sp<FakeApplicationHandle> application = new FakeApplicationHandle();
 
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 6eda20d..e3b7f2f 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -167,6 +167,8 @@
     // from GLES
     const uint32_t mTextureName;
 
+    bool mRefreshPending{false};
+
 private:
     // Returns true if this layer requires filtering
     bool needsFiltering() const;
@@ -184,8 +186,6 @@
     // The texture used to draw the layer in GLES composition mode
     mutable renderengine::Texture mTexture;
 
-    bool mRefreshPending{false};
-
     Rect getBufferSize(const State& s) const override;
 };
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 5a61122..42021d1 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -352,6 +352,14 @@
 // Interface implementation for BufferLayerConsumer::ContentsChangedListener
 // -----------------------------------------------------------------------
 
+void BufferQueueLayer::fakeVsync() {
+    mRefreshPending = false;
+    bool ignored = false;
+    latchBuffer(ignored, systemTime(), Fence::NO_FENCE);
+    usleep(16000);
+    releasePendingBuffer(systemTime());
+}
+
 void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
     // Add this buffer from our internal queue tracker
     { // Autolock scope
@@ -390,10 +398,7 @@
     // If this layer is orphaned, then we run a fake vsync pulse so that
     // dequeueBuffer doesn't block indefinitely.
     if (isRemovedFromCurrentState()) {
-        bool ignored = false;
-        latchBuffer(ignored, systemTime(), Fence::NO_FENCE);
-        usleep(16000);
-        releasePendingBuffer(systemTime());
+        fakeVsync();
     } else {
         mFlinger->signalLayerUpdate();
     }
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index ae0b705..d7c3f6a 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -137,6 +137,8 @@
     // thread-safe
     std::atomic<int32_t> mQueuedFrames{0};
     std::atomic<bool> mSidebandStreamChanged{false};
+
+    void fakeVsync();
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 1683982..bf925b2 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -67,7 +67,13 @@
 }
 
 void EventThreadConnection::requestNextVsync() {
-    mEventThread->requestNextVsync(this);
+    ATRACE_NAME("requestNextVsync");
+    mEventThread->requestNextVsync(this, true);
+}
+
+void EventThreadConnection::requestNextVsyncForHWC() {
+    ATRACE_NAME("requestNextVsyncForHWC");
+    mEventThread->requestNextVsync(this, false);
 }
 
 status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
@@ -184,16 +190,17 @@
     }
 }
 
-void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (mResetIdleTimer) {
+void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection, bool reset) {
+    if (mResetIdleTimer && reset) {
+        ATRACE_NAME("resetIdleTimer");
         mResetIdleTimer();
     }
-
     if (mResyncWithRateLimitCallback) {
         mResyncWithRateLimitCallback();
     }
 
+    std::lock_guard<std::mutex> lock(mMutex);
+
     if (connection->count < 0) {
         connection->count = 0;
         mCondition.notify_all();
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 66f54bd..e110488 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -68,6 +68,9 @@
     status_t stealReceiveChannel(gui::BitTube* outChannel) override;
     status_t setVsyncRate(uint32_t count) override;
     void requestNextVsync() override; // asynchronous
+    // Requesting Vsync for HWC does not reset the idle timer, since HWC requires a refresh
+    // in order to update the configs.
+    void requestNextVsyncForHWC();
 
     // count >= 1 : continuous event. count is the vsync rate
     // count == 0 : one-shot event that has not fired
@@ -105,7 +108,9 @@
     virtual status_t registerDisplayEventConnection(
             const sp<EventThreadConnection>& connection) = 0;
     virtual void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) = 0;
-    virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0;
+    // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
+    virtual void requestNextVsync(const sp<EventThreadConnection>& connection,
+                                  bool resetIdleTimer) = 0;
 };
 
 namespace impl {
@@ -129,7 +134,8 @@
 
     status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
     void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) override;
-    void requestNextVsync(const sp<EventThreadConnection>& connection) override;
+    void requestNextVsync(const sp<EventThreadConnection>& connection,
+                          bool resetIdleTimer) override;
 
     // called before the screen is turned off from main thread
     void onScreenReleased() override;
@@ -166,6 +172,9 @@
     // Implements VSyncSource::Callback
     void onVSyncEvent(nsecs_t timestamp) override;
 
+    // Acquires mutex and requests next vsync.
+    void requestNextVsyncInternal(const sp<EventThreadConnection>& connection) EXCLUDES(mMutex);
+
     // TODO(b/113612090): Once the Scheduler is complete this pointer will become obsolete.
     VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
     std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 36403cc..66f42bb 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -148,6 +148,10 @@
     mEvents->requestNextVsync();
 }
 
+void MessageQueue::invalidateForHWC() {
+    mEvents->requestNextVsyncForHWC();
+}
+
 void MessageQueue::refresh() {
     mHandler->dispatchRefresh();
 }
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 24a3834..0bf00b0 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -91,6 +91,7 @@
     virtual void waitMessage() = 0;
     virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
     virtual void invalidate() = 0;
+    virtual void invalidateForHWC() = 0;
     virtual void refresh() = 0;
 };
 
@@ -134,6 +135,9 @@
 
     // sends INVALIDATE message at next VSYNC
     void invalidate() override;
+
+    // sends INVALIDATE message at next VSYNC, without resetting the idle timer in the Scheduler
+    void invalidateForHWC();
     // sends REFRESH message at next VSYNC
     void refresh() override;
 };
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index c363ba5..fec53af 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -238,6 +238,16 @@
     mLayerHistory.incrementCounter();
 }
 
+void Scheduler::setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback) {
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    mExpiredTimerCallback = expiredTimerCallback;
+}
+
+void Scheduler::setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback) {
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    mResetTimerCallback = resetTimerCallback;
+}
+
 void Scheduler::updateFrameSkipping(const int64_t skipCount) {
     ATRACE_INT("FrameSkipCount", skipCount);
     if (mSkipCount != skipCount) {
@@ -314,12 +324,12 @@
     // TODO(b/113612090): This are current numbers from trial and error while running videos
     // from YouTube at 24, 30, and 60 fps.
     if (mean > 14 && mean < 18) {
-        ATRACE_INT("FPS", 60);
+        ATRACE_INT("MediaFPS", 60);
     } else if (mean > 31 && mean < 34) {
-        ATRACE_INT("FPS", 30);
+        ATRACE_INT("MediaFPS", 30);
         return;
     } else if (mean > 39 && mean < 42) {
-        ATRACE_INT("FPS", 24);
+        ATRACE_INT("MediaFPS", 24);
     }
 }
 
@@ -328,13 +338,19 @@
         mIdleTimer->reset();
         ATRACE_INT("ExpiredIdleTimer", 0);
     }
+
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    if (mResetTimerCallback) {
+        mResetTimerCallback();
+    }
 }
 
 void Scheduler::expiredTimerCallback() {
-    // TODO(b/113612090): Each time a timer expired, we should record the information into
-    // a circular buffer. Once this has happened a given amount (TBD) of times, we can comfortably
-    // say that the device is sitting in idle.
-    ATRACE_INT("ExpiredIdleTimer", 1);
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    if (mExpiredTimerCallback) {
+        mExpiredTimerCallback();
+        ATRACE_INT("ExpiredIdleTimer", 1);
+    }
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 9d7dd4d..3538f31 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,6 +36,9 @@
 
 class Scheduler {
 public:
+    using ExpiredIdleTimerCallback = std::function<void()>;
+    using ResetIdleTimerCallback = std::function<void()>;
+
     // Enum to indicate whether to start the transaction early, or at vsync time.
     enum class TransactionStart { EARLY, NORMAL };
 
@@ -111,6 +114,10 @@
                                      const std::string layerName);
     // Increments counter in the layer history to indicate that SF has started a new frame.
     void incrementFrameCounter();
+    // Callback that gets invoked once the idle timer expires.
+    void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback);
+    // Callback that gets invoked once the idle timer is reset.
+    void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback);
 
 protected:
     virtual std::unique_ptr<EventThread> makeEventThread(
@@ -173,6 +180,10 @@
     // interval, a callback is fired. Set this variable to >0 to use this feature.
     int64_t mSetIdleTimerMs = 0;
     std::unique_ptr<scheduler::IdleTimer> mIdleTimer;
+
+    std::mutex mCallbackLock;
+    ExpiredIdleTimerCallback mExpiredTimerCallback GUARDED_BY(mCallbackLock);
+    ResetIdleTimerCallback mResetTimerCallback GUARDED_BY(mCallbackLock);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dc82b32..bd16d64 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -729,6 +729,11 @@
         ALOGE("Run StartPropertySetThread failed!");
     }
 
+    if (mUseScheduler) {
+        mScheduler->setExpiredIdleTimerCallback([this]() { setRefreshRateTo(60.f /* fps */); });
+        mScheduler->setResetIdleTimerCallback([this]() { setRefreshRateTo(90.f /* fps */); });
+    }
+
     ALOGV("Done initializing");
 }
 
@@ -934,14 +939,45 @@
     return display->getActiveConfig();
 }
 
-void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& display, int mode) {
+status_t SurfaceFlinger::setActiveConfigAsync(const sp<IBinder>& displayToken, int mode) {
+    ATRACE_NAME("setActiveConfigAsync");
+    postMessageAsync(new LambdaMessage([=] { setActiveConfigInternal(displayToken, mode); }));
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
+    ATRACE_NAME("setActiveConfigSync");
+    postMessageSync(new LambdaMessage([&] { setActiveConfigInternal(displayToken, mode); }));
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) {
+    Vector<DisplayInfo> configs;
+    getDisplayConfigs(displayToken, &configs);
+    if (mode < 0 || mode >= static_cast<int>(configs.size())) {
+        ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size());
+        return;
+    }
+
+    const auto display = getDisplayDevice(displayToken);
+    if (!display) {
+        ALOGE("Attempt to set active config %d for invalid display token %p", mode,
+              displayToken.get());
+        return;
+    }
     if (display->isVirtual()) {
-        ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
+        ALOGW("Attempt to set active config %d for virtual display", mode);
+        return;
+    }
+    int currentDisplayPowerMode = display->getPowerMode();
+    if (currentDisplayPowerMode != HWC_POWER_MODE_NORMAL) {
+        // Don't change active config when in AoD.
         return;
     }
 
     int currentMode = display->getActiveConfig();
     if (mode == currentMode) {
+        // Don't update config if we are already running in the desired mode.
         return;
     }
 
@@ -950,29 +986,9 @@
 
     display->setActiveConfig(mode);
     getHwComposer().setActiveConfig(*displayId, mode);
-}
 
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
-    postMessageSync(new LambdaMessage([&] {
-        Vector<DisplayInfo> configs;
-        getDisplayConfigs(displayToken, &configs);
-        if (mode < 0 || mode >= static_cast<int>(configs.size())) {
-            ALOGE("Attempt to set active config %d for display with %zu configs", mode,
-                  configs.size());
-            return;
-        }
-        const auto display = getDisplayDevice(displayToken);
-        if (!display) {
-            ALOGE("Attempt to set active config %d for invalid display token %p", mode,
-                  displayToken.get());
-        } else if (display->isVirtual()) {
-            ALOGW("Attempt to set active config %d for virtual display", mode);
-        } else {
-            setActiveConfigInternal(display, mode);
-        }
-    }));
-
-    return NO_ERROR;
+    ATRACE_INT("ActiveConfigMode", mode);
+    resyncToHardwareVsync(true);
 }
 
 status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1382,6 +1398,49 @@
     *compositorTiming = getBE().mCompositorTiming;
 }
 
+void SurfaceFlinger::setRefreshRateTo(float newFps) {
+    const auto displayId = getInternalDisplayId();
+    if (!displayId || mBootStage != BootStage::FINISHED) {
+        return;
+    }
+    // TODO(b/113612090): There should be a message queue flush here. Because this esentially
+    // runs on a mainthread, we cannot call postMessageSync. This can be resolved in a better
+    // manner, once the setActiveConfig is synchronous, and is executed at a known time in a
+    // refresh cycle.
+
+    // Don't do any updating if the current fps is the same as the new one.
+    const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
+    const nsecs_t currentVsyncPeriod = activeConfig->getVsyncPeriod();
+    if (currentVsyncPeriod == 0) {
+        return;
+    }
+    // TODO(b/113612090): Consider having an enum value for correct refresh rates, rather than
+    // floating numbers.
+    const float currentFps = 1e9 / currentVsyncPeriod;
+    if (std::abs(currentFps - newFps) <= 1) {
+        return;
+    }
+
+    auto configs = getHwComposer().getConfigs(*displayId);
+    for (int i = 0; i < configs.size(); i++) {
+        const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod();
+        if (vsyncPeriod == 0) {
+            continue;
+        }
+        const float fps = 1e9 / vsyncPeriod;
+        // TODO(b/113612090): There should be a better way at determining which config
+        // has the right refresh rate.
+        if (std::abs(fps - newFps) <= 1) {
+            const auto display = getBuiltInDisplay(HWC_DISPLAY_PRIMARY);
+            if (!display) return;
+            // This is posted in async function to avoid deadlock when getDisplayDevice
+            // requires mStateLock.
+            setActiveConfigAsync(display, i);
+            ATRACE_INT("FPS", newFps);
+        }
+    }
+}
+
 void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
                                        HWC2::Connection connection) {
     ALOGV("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
@@ -1413,7 +1472,7 @@
     if (sequenceId != getBE().mComposerSequenceId) {
         return;
     }
-    repaintEverything();
+    repaintEverythingForHWC();
 }
 
 void SurfaceFlinger::setVsyncEnabled(EventThread::DisplayType /*displayType*/, bool enabled) {
@@ -5177,6 +5236,11 @@
     signalTransaction();
 }
 
+void SurfaceFlinger::repaintEverythingForHWC() {
+    mRepaintEverything = true;
+    mEventQueue->invalidateForHWC();
+}
+
 // A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
 class WindowDisconnector {
 public:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ac730ab..eb7127e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -311,6 +311,9 @@
     // force full composition on all displays
     void repaintEverything();
 
+    // force full composition on all displays without resetting the scheduler idle timer.
+    void repaintEverythingForHWC();
+
     surfaceflinger::Factory& getFactory() { return mFactory; }
 
     // The CompositionEngine encapsulates all composition related interfaces and actions.
@@ -502,8 +505,10 @@
 
     // called on the main thread in response to initializeDisplays()
     void onInitializeDisplays();
+    // setActiveConfigInternal() posted on a main thread for async execution
+    status_t setActiveConfigAsync(const sp<IBinder>& displayToken, int mode);
     // called on the main thread in response to setActiveConfig()
-    void setActiveConfigInternal(const sp<DisplayDevice>& display, int mode);
+    void setActiveConfigInternal(const sp<IBinder>& displayToken, int mode);
     // called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, int mode);
 
@@ -763,6 +768,9 @@
     void resyncWithRateLimit();
     void getCompositorTiming(CompositorTiming* compositorTiming);
 private:
+    // Sets the refresh rate to newFps by switching active configs, if they are available for
+    // the desired refresh rate.
+    void setRefreshRateTo(float newFps);
 
     /* ------------------------------------------------------------------------
      * Debugging & dumpsys
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index acbed51..2d26bb3 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -200,7 +200,7 @@
 
 TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
     // Signal that we want the next vsync event to be posted to the connection
-    mThread->requestNextVsync(mConnection);
+    mThread->requestNextVsync(mConnection, false);
 
     // EventThread should immediately request a resync.
     EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index bb6e183..48d45fa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -37,7 +37,7 @@
     MOCK_METHOD1(registerDisplayEventConnection,
                  status_t(const sp<android::EventThreadConnection> &));
     MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
-    MOCK_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &));
+    MOCK_METHOD2(requestNextVsync, void(const sp<android::EventThreadConnection> &, bool));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index dc8d606..8c113e2 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -35,6 +35,7 @@
     MOCK_METHOD0(waitMessage, void());
     MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
     MOCK_METHOD0(invalidate, void());
+    MOCK_METHOD0(invalidateForHWC, void());
     MOCK_METHOD0(refresh, void());
 };