SF: Fix UAF due to outliving idle timer

On Scheduler destruction, stop the timer and clear the callbacks, since
the RefreshRateConfigs may outlive the Scheduler.

On RefreshRateConfigs destruction, ensure the timer is destroyed before
the mutex/callbacks. Invoke the callback under lock.

Remove TestableSurfaceFlinger's subtle precondition that setupScheduler
must be called prior to FakeDisplayDeviceInjector for them to share
RefreshRateConfigs, which was not the case in DisplayModeSwitchingTest.

TestableScheduler is deleted through a Scheduler pointer, so ~Scheduler
should be virtual. Although ~TestableScheduler is trivial, this invoked
undefined behavior.

Bug: 213688734
Test: libsurfaceflinger_unittest --gtest_repeat=1000
Change-Id: Id23fafaf3d7071a5e28e275de386dd731a726006
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index cbe4552..4173088 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -62,6 +62,15 @@
 Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
       : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {}
 
+Scheduler::~Scheduler() {
+    // Stop timers and wait for their threads to exit.
+    mDisplayPowerTimer.reset();
+    mTouchTimer.reset();
+
+    // Stop idle timer and clear callbacks, as the RefreshRateConfigs may outlive the Scheduler.
+    setRefreshRateConfigs(nullptr);
+}
+
 void Scheduler::startTimers() {
     using namespace sysprop;
     using namespace std::string_literals;
@@ -84,11 +93,32 @@
     }
 }
 
-Scheduler::~Scheduler() {
-    // Ensure the OneShotTimer threads are joined before we start destroying state.
-    mDisplayPowerTimer.reset();
-    mTouchTimer.reset();
-    mRefreshRateConfigs.reset();
+void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) {
+    {
+        // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        if (mRefreshRateConfigs) {
+            mRefreshRateConfigs->stopIdleTimer();
+            mRefreshRateConfigs->clearIdleTimerCallbacks();
+        }
+    }
+    {
+        // Clear state that depends on the current instance.
+        std::scoped_lock lock(mPolicyLock);
+        mPolicy = {};
+    }
+
+    std::scoped_lock lock(mRefreshRateConfigsLock);
+    mRefreshRateConfigs = std::move(configs);
+    if (!mRefreshRateConfigs) return;
+
+    mRefreshRateConfigs->setIdleTimerCallbacks(
+            {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+                          .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+             .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+                        .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+
+    mRefreshRateConfigs->startIdleTimer();
 }
 
 void Scheduler::run() {