Merge "Use unordered_map for connections"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 519b13b..09aee89 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -118,6 +118,8 @@
         "kill",
         "librank",
         "logcat",
+        "lpdump",
+        "lpdumpd",
         "lsmod",
         "lsof",
         "netstat",
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index b781da3..fb476db 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -929,6 +929,14 @@
     RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
 }
 
+static void DumpDynamicPartitionInfo() {
+    if (!::android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
+        return;
+    }
+
+    RunCommand("LPDUMP", {"lpdump", "--all"});
+}
+
 static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_dir) {
     MYLOGD("AddAnrTraceDir(): dump_traces_file=%s, anr_traces_dir=%s\n", dump_traces_path,
            anr_traces_dir.c_str());
@@ -1515,6 +1523,7 @@
     }
     add_mountinfo();
     DumpIpTablesAsRoot();
+    DumpDynamicPartitionInfo();
 
     // Capture any IPSec policies in play. No keys are exposed here.
     RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, CommandOptions::WithTimeout(10).Build());
@@ -3496,7 +3505,7 @@
 
     struct sockaddr addr;
     socklen_t alen = sizeof(addr);
-    int fd = accept(s, &addr, &alen);
+    int fd = accept4(s, &addr, &alen, SOCK_CLOEXEC);
 
     // Close socket just after accept(), to make sure that connect() by client will get error
     // when the socket is used by the other services.
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
new file mode 100644
index 0000000..40b8dc7
--- /dev/null
+++ b/cmds/idlcli/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2019 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.
+
+cc_defaults {
+    name: "idlcli-defaults",
+    shared_libs: [
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vibrator@1.2",
+        "android.hardware.vibrator@1.3",
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    cflags: [
+        "-DLOG_TAG=\"idlcli\"",
+    ],
+}
+
+cc_library {
+    name: "libidlcli",
+    defaults: ["idlcli-defaults"],
+    srcs: [
+        "CommandVibrator.cpp",
+        "vibrator/CommandOff.cpp",
+        "vibrator/CommandOn.cpp",
+        "vibrator/CommandPerform.cpp",
+        "vibrator/CommandSetAmplitude.cpp",
+        "vibrator/CommandSetExternalControl.cpp",
+        "vibrator/CommandSupportsAmplitudeControl.cpp",
+        "vibrator/CommandSupportsExternalControl.cpp",
+    ],
+    visibility: [":__subpackages__"],
+}
+
+cc_binary {
+    name: "idlcli",
+    defaults: ["idlcli-defaults"],
+    srcs: ["main.cpp"],
+    whole_static_libs: ["libidlcli"],
+}
diff --git a/cmds/idlcli/CommandVibrator.cpp b/cmds/idlcli/CommandVibrator.cpp
new file mode 100644
index 0000000..a7a70c3
--- /dev/null
+++ b/cmds/idlcli/CommandVibrator.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "utils.h"
+
+namespace android {
+namespace idlcli {
+
+class IdlCli;
+
+class CommandVibrator : public CommandWithSubcommands<CommandVibrator> {
+    std::string getDescription() const override { return "Invoke Vibrator HIDL APIs."; }
+
+    std::string getUsageSummary() const override { return "<api> [arguments]"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<api>", CommandRegistry<CommandVibrator>::List()},
+        };
+        return details;
+    }
+};
+
+static const auto Command = CommandRegistry<IdlCli>::Register<CommandVibrator>("vibrator");
+
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/IdlCli.h b/cmds/idlcli/IdlCli.h
new file mode 100644
index 0000000..dd84304
--- /dev/null
+++ b/cmds/idlcli/IdlCli.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
+#define FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
+
+#include "utils.h"
+
+namespace android {
+namespace idlcli {
+
+class IdlCli : public CommandWithSubcommands<IdlCli> {
+    std::string getDescription() const override { return "Invoke IDL APIs."; }
+
+    std::string getUsageSummary() const override { return "<idl> [arguments]"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<idl>", CommandRegistry<IdlCli>::List()},
+        };
+        return details;
+    }
+};
+
+} // namespace idlcli
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
diff --git a/cmds/idlcli/main.cpp b/cmds/idlcli/main.cpp
new file mode 100644
index 0000000..9ed9d82
--- /dev/null
+++ b/cmds/idlcli/main.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "IdlCli.h"
+#include "utils.h"
+
+int main(const int argc, const char* const argv[]) {
+    using namespace ::android::idlcli;
+    return IdlCli{}.main(Args{argc, argv});
+}
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
new file mode 100644
index 0000000..a8e5954
--- /dev/null
+++ b/cmds/idlcli/utils.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+
+#include <hidl/HidlSupport.h>
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace idlcli {
+
+namespace overrides {
+
+namespace details {
+
+template <typename T>
+inline std::istream &operator>>(std::istream &stream, T &out) {
+    auto pos = stream.tellg();
+    auto tmp = +out;
+    auto min = +std::numeric_limits<T>::min();
+    auto max = +std::numeric_limits<T>::max();
+    stream >> tmp;
+    if (!stream) {
+        return stream;
+    }
+    if (tmp < min || tmp > max) {
+        stream.seekg(pos);
+        stream.setstate(std::ios_base::failbit);
+        return stream;
+    }
+    out = tmp;
+    return stream;
+}
+
+} // namespace details
+
+// override for default behavior of treating as a character
+inline std::istream &operator>>(std::istream &stream, int8_t &out) {
+    return details::operator>>(stream, out);
+}
+
+// override for default behavior of treating as a character
+inline std::istream &operator>>(std::istream &stream, uint8_t &out) {
+    return details::operator>>(stream, out);
+}
+
+} // namespace overrides
+
+template <typename T, typename R = hardware::hidl_enum_range<T>>
+inline std::istream &operator>>(std::istream &stream, T &out) {
+    using overrides::operator>>;
+    auto validRange = R();
+    auto pos = stream.tellg();
+    std::underlying_type_t<T> in;
+    T tmp;
+    stream >> in;
+    if (!stream) {
+        return stream;
+    }
+    tmp = static_cast<T>(in);
+    if (tmp < *validRange.begin() || tmp > *std::prev(validRange.end())) {
+        stream.seekg(pos);
+        stream.setstate(std::ios_base::failbit);
+        return stream;
+    }
+    out = tmp;
+    return stream;
+}
+
+enum Status : unsigned int {
+    OK,
+    USAGE,
+    UNAVAILABLE,
+    ERROR,
+};
+
+class Args {
+public:
+    Args(const int argc, const char *const argv[]) {
+        for (int argi = 0; argi < argc; argi++) {
+            mArgs.emplace_back(std::string_view(argv[argi]));
+        }
+    }
+
+    template <typename T = std::string>
+    std::optional<T> get() {
+        return get<T>(false);
+    }
+
+    template <typename T = std::string>
+    std::optional<T> pop() {
+        return get<T>(true);
+    }
+
+    bool empty() { return mArgs.empty(); }
+
+private:
+    template <typename T>
+    std::optional<T> get(bool erase) {
+        using idlcli::operator>>;
+        using overrides::operator>>;
+        T retValue;
+
+        if (mArgs.empty()) {
+            return {};
+        }
+
+        std::stringstream stream{std::string{mArgs.front()}};
+        stream >> std::setbase(0) >> retValue;
+        if (!stream || !stream.eof()) {
+            return {};
+        }
+
+        if (erase) {
+            mArgs.erase(mArgs.begin());
+        }
+
+        return retValue;
+    }
+
+    std::vector<std::string_view> mArgs;
+};
+
+class Command {
+protected:
+    struct Usage {
+        std::string name;
+        std::vector<std::string> details;
+    };
+    using UsageDetails = std::vector<Usage>;
+
+public:
+    virtual ~Command() = default;
+
+    Status main(Args &&args) {
+        Status status = doArgsAndMain(std::move(args));
+        if (status == USAGE) {
+            printUsage();
+            return ERROR;
+        }
+        if (status == UNAVAILABLE) {
+            std::cerr << "The requested operation is unavailable." << std::endl;
+            return ERROR;
+        }
+        return status;
+    }
+
+private:
+    virtual std::string getDescription() const = 0;
+    virtual std::string getUsageSummary() const = 0;
+    virtual UsageDetails getUsageDetails() const = 0;
+    virtual Status doArgs(Args &args) = 0;
+    virtual Status doMain(Args &&args) = 0;
+
+    void printUsage() const {
+        std::cerr << "Description:\n  " << getDescription() << std::endl;
+        std::cerr << "Usage:\n  " << mName << " " << getUsageSummary() << std::endl;
+
+        std::cerr << "Details:" << std::endl;
+        size_t entryNameWidth = 0;
+        for (auto &entry : getUsageDetails()) {
+            entryNameWidth = std::max(entryNameWidth, entry.name.length());
+        }
+        for (auto &entry : getUsageDetails()) {
+            auto prefix = entry.name;
+            for (auto &line : entry.details) {
+                std::cerr << "  " << std::left << std::setw(entryNameWidth + 8) << prefix << line
+                          << std::endl;
+                prefix = "";
+            }
+        }
+    }
+
+    Status doArgsAndMain(Args &&args) {
+        Status status;
+        mName = *args.pop();
+        if ((status = doArgs(args)) != OK) {
+            return status;
+        }
+        if ((status = doMain(std::move(args))) != OK) {
+            return status;
+        }
+        return OK;
+    }
+
+protected:
+    std::string mName;
+};
+
+template <typename T>
+class CommandRegistry {
+private:
+    using CommandCreator = std::function<std::unique_ptr<Command>()>;
+
+public:
+    template <typename U>
+    static CommandCreator Register(const std::string name) {
+        Instance()->mCommands[name] = [] { return std::make_unique<U>(); };
+        return Instance()->mCommands[name];
+    }
+
+    static std::unique_ptr<Command> Create(const std::string name) {
+        auto it = Instance()->mCommands.find(name);
+        if (it == Instance()->mCommands.end()) {
+            return nullptr;
+        }
+        return it->second();
+    }
+
+    static auto List() {
+        std::vector<std::string> list;
+        for (auto &it : Instance()->mCommands) {
+            list.push_back(it.first);
+        }
+        std::sort(list.begin(), list.end());
+        return list;
+    }
+
+private:
+    static CommandRegistry *Instance() {
+        static CommandRegistry sRegistry;
+        return &sRegistry;
+    }
+
+private:
+    std::map<const std::string, CommandCreator> mCommands;
+};
+
+template <typename T>
+class CommandWithSubcommands : public Command {
+private:
+    Status doArgs(Args &args) override {
+        mCommand = CommandRegistry<T>::Create(*args.get());
+        if (!mCommand) {
+            std::cerr << "Invalid Command!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args &&args) override { return mCommand->main(std::move(args)); }
+
+protected:
+    std::unique_ptr<Command> mCommand;
+};
+
+} // namespace idlcli
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
new file mode 100644
index 0000000..bcb207b
--- /dev/null
+++ b/cmds/idlcli/vibrator.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+
+#include "utils.h"
+
+#include "log/log.h"
+
+namespace android {
+
+using hardware::Return;
+
+static constexpr int NUM_TRIES = 2;
+
+// Creates a Return<R> with STATUS::EX_NULL_POINTER.
+template <class R>
+inline Return<R> NullptrStatus() {
+    using ::android::hardware::Status;
+    return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)};
+}
+
+template <typename I>
+class HalWrapper {
+public:
+    static std::unique_ptr<HalWrapper> Create() {
+        // Assume that if getService returns a nullptr, HAL is not available on the
+        // device.
+        auto hal = I::getService();
+        return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
+    }
+
+    template <class R, class... Args0, class... Args1>
+    Return<R> call(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+        return (*mHal.*fn)(std::forward<Args1>(args1)...);
+    }
+
+private:
+    HalWrapper(sp<I>&& hal) : mHal(std::move(hal)) {}
+
+private:
+    sp<I> mHal;
+};
+
+template <typename I>
+static auto getHal() {
+    static auto sHalWrapper = HalWrapper<I>::Create();
+    return sHalWrapper.get();
+}
+
+template <class R, class I, class... Args0, class... Args1>
+Return<R> halCall(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+    auto hal = getHal<I>();
+    return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
+}
+
+namespace idlcli {
+namespace vibrator {
+
+namespace V1_0 = ::android::hardware::vibrator::V1_0;
+namespace V1_1 = ::android::hardware::vibrator::V1_1;
+namespace V1_2 = ::android::hardware::vibrator::V1_2;
+namespace V1_3 = ::android::hardware::vibrator::V1_3;
+
+} // namespace vibrator
+} // namespace idlcli
+
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
new file mode 100644
index 0000000..a674f01
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandOff : public Command {
+    std::string getDescription() const override { return "Turn off vibrator."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::off);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandOff>("off");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
new file mode 100644
index 0000000..2164b7d
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandOn : public Command {
+    std::string getDescription() const override { return "Turn on vibrator."; }
+
+    std::string getUsageSummary() const override { return "<duration>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<duration>", {"In milliseconds."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto duration = args.pop<decltype(mDuration)>()) {
+            mDuration = *duration;
+        } else {
+            std::cerr << "Missing or Invalid Duration!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::on, mDuration);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    uint32_t mDuration;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandOn>("on");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
new file mode 100644
index 0000000..688cbd8
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using V1_0::EffectStrength;
+using V1_3::Effect;
+
+class CommandPerform : public Command {
+    std::string getDescription() const override { return "Perform vibration effect."; }
+
+    std::string getUsageSummary() const override { return "<effect> <strength>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<effect>", {"Effect ID."}},
+                {"<strength>", {"0-2."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto effect = args.pop<decltype(mEffect)>()) {
+            mEffect = *effect;
+            std::cout << "Effect: " << toString(mEffect) << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Effect!" << std::endl;
+            return USAGE;
+        }
+        if (auto strength = args.pop<decltype(mStrength)>()) {
+            mStrength = *strength;
+            std::cout << "Strength: " << toString(mStrength) << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Strength!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        Return<void> ret;
+        V1_0::Status status;
+        uint32_t lengthMs;
+        auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+            status = retStatus;
+            lengthMs = retLengthMs;
+        };
+
+        if (auto hal = getHal<V1_3::IVibrator>()) {
+            ret = hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
+                            mStrength, callback);
+        } else if (auto hal = getHal<V1_2::IVibrator>()) {
+            ret = hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
+                            mStrength, callback);
+        } else if (auto hal = getHal<V1_1::IVibrator>()) {
+            ret = hal->call(&V1_1::IVibrator::perform_1_1, static_cast<V1_1::Effect_1_1>(mEffect),
+                            mStrength, callback);
+        } else if (auto hal = getHal<V1_0::IVibrator>()) {
+            ret = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
+                            mStrength, callback);
+        } else {
+            ret = NullptrStatus<void>();
+        }
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(status) << std::endl;
+        std::cout << "Length: " << lengthMs << std::endl;
+
+        return status == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    Effect mEffect;
+    EffectStrength mStrength;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandPerform>("perform");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
new file mode 100644
index 0000000..38a1dc2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSetAmplitude : public Command {
+    std::string getDescription() const override { return "Set vibration amplitude."; }
+
+    std::string getUsageSummary() const override { return "<amplitude>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<amplitude>", {"1-255."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto amplitude = args.pop<decltype(mAmplitude)>()) {
+            mAmplitude = *amplitude;
+        } else {
+            std::cerr << "Missing or Invalid Amplitude!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::setAmplitude, mAmplitude);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    uint8_t mAmplitude;
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSetAmplitude>("setAmplitude");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
new file mode 100644
index 0000000..5fb1fac
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSetExternalControl : public Command {
+    std::string getDescription() const override {
+        return "Enable/disable vibration external control.";
+    }
+
+    std::string getUsageSummary() const override { return "<enable>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<enable>", {"0/1."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto enable = args.pop<decltype(mEnable)>()) {
+            mEnable = *enable;
+        } else {
+            std::cerr << "Missing Enable!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_3::IVibrator::setExternalControl, mEnable);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    bool mEnable;
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSetExternalControl>("setExternalControl");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
new file mode 100644
index 0000000..cdc529a
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSupportsAmplitudeControl : public Command {
+    std::string getDescription() const override { return "Check support for amplitude control."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::supportsAmplitudeControl);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Result: " << std::boolalpha << ret << std::endl;
+
+        return OK;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSupportsAmplitudeControl>(
+                "supportsAmplitudeControl");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
new file mode 100644
index 0000000..ed15d76
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSupportsExternalControl : public Command {
+    std::string getDescription() const override { return "Check support for external control."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_3::IVibrator::supportsExternalControl);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Result: " << std::boolalpha << ret << std::endl;
+
+        return OK;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSupportsExternalControl>(
+                "supportsExternalControl");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 9f6193b..11d43a6 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -36,8 +36,6 @@
 
     const char* driver = argc == 2 ? argv[1] : "/dev/binder";
 
-    android::base::InitLogging(nullptr, &android::base::KernelLogger);
-
     sp<ProcessState> ps = ProcessState::initWithDriver(driver);
     ps->setThreadPoolMaxThreadCount(0);
     ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index f11cf62..642c5f2 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -33,7 +33,7 @@
     ],
 
     header_libs: [
-        "libnativeloader-dummy-headers",
+        "libnativeloader-headers",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 7e356e4..aad1849 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1081,6 +1081,9 @@
     case NATIVE_WINDOW_SET_AUTO_PREROTATION:
         res = dispatchSetAutoPrerotation(args);
         break;
+    case NATIVE_WINDOW_GET_LAST_DEQUEUE_START:
+        res = dispatchGetLastDequeueStartTime(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1286,6 +1289,12 @@
     return setAutoPrerotation(autoPrerotation);
 }
 
+int Surface::dispatchGetLastDequeueStartTime(va_list args) {
+    int64_t* lastDequeueStartTime = va_arg(args, int64_t*);
+    *lastDequeueStartTime = mLastDequeueStartTime;
+    return NO_ERROR;
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
@@ -1950,11 +1959,6 @@
     return mGraphicBufferProducer->getConsumerUsage(outUsage);
 }
 
-nsecs_t Surface::getLastDequeueStartTime() const {
-    Mutex::Autolock lock(mMutex);
-    return mLastDequeueStartTime;
-}
-
 status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) {
     if (out == nullptr) {
         ALOGE("%s: out must not be null!", __FUNCTION__);
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 28f5a26..5bcfcda 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -179,9 +179,6 @@
     status_t getUniqueId(uint64_t* outId) const;
     status_t getConsumerUsage(uint64_t* outUsage) const;
 
-    // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call
-    nsecs_t getLastDequeueStartTime() const;
-
 protected:
     virtual ~Surface();
 
@@ -247,6 +244,7 @@
     int dispatchGetHdrSupport(va_list args);
     int dispatchGetConsumerUsage64(va_list args);
     int dispatchSetAutoPrerotation(va_list args);
+    int dispatchGetLastDequeueStartTime(va_list args);
     bool transformToDisplayInverse();
 
 protected:
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 386f731..2ab34da 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -435,6 +435,9 @@
     surface->expectTap(1, 1);
 }
 
+/**
+ * TODO(b/139494112) fix tests once we define expected behavior
+ *
 // Ensure we send the input to the right surface when the surface visibility changes due to the
 // first buffer being submitted. ref: b/120839715
 TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) {
@@ -486,6 +489,7 @@
     injectTap(11, 11);
     bgSurface->expectTap(1, 1);
 }
+*/
 
 TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) {
     std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 5a121d7..0b61d92 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -617,7 +617,7 @@
     anw->dequeueBuffer(anw.get(), &buffer, &fenceFd);
     nsecs_t after = systemTime(CLOCK_MONOTONIC);
 
-    nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime();
+    nsecs_t lastDequeueTime = ANativeWindow_getLastDequeueStartTime(anw.get());
     ASSERT_LE(before, lastDequeueTime);
     ASSERT_GE(after, lastDequeueTime);
 }
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 4c59e6c..3b195f7 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -274,3 +274,13 @@
 int ANativeWindow_getLastDequeueDuration(ANativeWindow* window) {
     return query(window, NATIVE_WINDOW_LAST_DEQUEUE_DURATION);
 }
+
+int ANativeWindow_getLastQueueDuration(ANativeWindow* window) {
+    return query(window, NATIVE_WINDOW_LAST_QUEUE_DURATION);
+}
+
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window) {
+    int64_t time;
+    int success = window->perform(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_START, &time);
+    return success < 0 ? success : time;
+}
diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h
index 0260cbc..3ddd549 100644
--- a/libs/nativewindow/include/apex/window.h
+++ b/libs/nativewindow/include/apex/window.h
@@ -31,4 +31,21 @@
  */
 int ANativeWindow_getLastDequeueDuration(ANativeWindow* window);
 
+/**
+ * Retrieves how long it took for the last time a buffer was queued.
+ *
+ * \return a negative value on error, otherwise returns the duration in
+ * microseconds
+ */
+int ANativeWindow_getLastQueueDuration(ANativeWindow* window);
+
+/**
+ * Retrieves the system time in nanoseconds when the last time a buffer
+ * was dequeued.
+ *
+ * \return a negative value on error, otherwise returns the duration in
+ * nanoseconds.
+ */
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window);
+
 __END_DECLS
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 78354bb..b7ae28a 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -93,7 +93,6 @@
      */
     NATIVE_WINDOW_CONCRETE_TYPE = 5,
 
-
     /*
      * Default width and height of ANativeWindow buffers, these are the
      * dimensions of the window buffers irrespective of the
@@ -240,6 +239,7 @@
     NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA   = 33,
     NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
     NATIVE_WINDOW_SET_AUTO_PREROTATION            = 35,
+    NATIVE_WINDOW_GET_LAST_DEQUEUE_START          = 36,    /* private */
     // clang-format on
 };
 
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 7fe4df0..33c6400 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -23,6 +23,8 @@
     ANativeWindow_getFormat;
     ANativeWindow_getHeight;
     ANativeWindow_getLastDequeueDuration; # apex # introduced=30
+    ANativeWindow_getLastDequeueStartTime; # apex # introduced=30
+    ANativeWindow_getLastQueueDuration; # apex # introduced=30
     ANativeWindow_getWidth;
     ANativeWindow_lock;
     ANativeWindow_query; # vndk
diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp
index 5247e04..947217d 100644
--- a/libs/nativewindow/tests/ANativeWindowTest.cpp
+++ b/libs/nativewindow/tests/ANativeWindowTest.cpp
@@ -36,6 +36,12 @@
 
     // Exposes the internal last dequeue duration that's stored on the Surface.
     nsecs_t getLastDequeueDuration() const { return mLastDequeueDuration; }
+
+    // Exposes the internal last queue duration that's stored on the Surface.
+    nsecs_t getLastQueueDuration() const { return mLastQueueDuration; }
+
+    // Exposes the internal last dequeue start time that's stored on the Surface.
+    nsecs_t getLastDequeueStartTime() const { return mLastDequeueStartTime; }
 };
 
 class ANativeWindowTest : public ::testing::Test {
@@ -82,3 +88,53 @@
     EXPECT_GT(result, 0);
     EXPECT_EQ(result, mWindow->getLastDequeueDuration() / 1000);
 }
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_noDequeue_returnsZero) {
+    int result = ANativeWindow_getLastQueueDuration(mWindow.get());
+    EXPECT_EQ(0, result);
+    EXPECT_EQ(0, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_noQueue_returnsZero) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, result);
+
+    result = ANativeWindow_getLastQueueDuration(mWindow.get());
+    EXPECT_EQ(result, 0);
+    EXPECT_EQ(result, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_withQueue_returnsTime) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, result);
+
+    result = ANativeWindow_queueBuffer(mWindow.get(), buffer, 0);
+
+    result = ANativeWindow_getLastQueueDuration(mWindow.get());
+    EXPECT_GT(result, 0);
+    EXPECT_EQ(result, mWindow->getLastQueueDuration() / 1000);
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueStartTime_noDequeue_returnsZero) {
+    int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get());
+    EXPECT_EQ(0, result);
+    EXPECT_EQ(0, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueStartTime_withDequeue_returnsTime) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, dequeueResult);
+
+    int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get());
+    EXPECT_GT(result, 0);
+    EXPECT_EQ(result, mWindow->getLastDequeueStartTime());
+}
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 1cbf78e..d59f274 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -1141,13 +1141,24 @@
                 }
             }
 
-            setEnabled(enabled, when);
+            if (changes) {
+                // For first-time configuration, only allow device to be disabled after mappers have
+                // finished configuring. This is because we need to read some of the properties from
+                // the device's open fd.
+                setEnabled(enabled, when);
+            }
         }
 
         for (InputMapper* mapper : mMappers) {
             mapper->configure(when, config, changes);
             mSources |= mapper->getSources();
         }
+
+        // If a device is just plugged but it might be disabled, we need to update some info like
+        // axis range of touch from each InputMapper first, then disable it.
+        if (!changes) {
+            setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
+        }
     }
 }
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 348a12b..09df2a7 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -527,7 +527,7 @@
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const {
         Device* device = getDevice(deviceId);
-        if (device) {
+        if (device && device->enabled) {
             ssize_t index = device->absoluteAxes.indexOfKey(axis);
             if (index >= 0) {
                 *outAxisInfo = device->absoluteAxes.valueAt(index);
@@ -6554,4 +6554,35 @@
     ASSERT_EQ(frames, motionArgs.videoFrames);
 }
 
+/**
+ * If we had defined port associations, but the viewport is not ready, the touch device would be
+ * expected to be disabled, and it should be enabled after the viewport has found.
+ */
+TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    constexpr uint8_t hdmi2 = 1;
+    const std::string secondaryUniqueId = "uniqueId2";
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2);
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    ASSERT_EQ(mDevice->isEnabled(), false);
+
+    // Add display on hdmi2, the device should be enabled and can receive touch event.
+    prepareSecondaryDisplay(type, hdmi2);
+    ASSERT_EQ(mDevice->isEnabled(), true);
+
+    // Send a touch event.
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 486b042..b060b04 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -34,6 +34,7 @@
 #include <cutils/properties.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 #include <renderengine/RenderEngine.h>
@@ -187,8 +188,7 @@
 
         // Query the texture matrix given our current filtering mode.
         float textureMatrix[16];
-        setFilteringEnabled(useFiltering);
-        memcpy(textureMatrix, mBufferInfo.mTransformMatrix, sizeof(mBufferInfo.mTransformMatrix));
+        getDrawingTransformMatrix(useFiltering, textureMatrix);
 
         if (getTransformToDisplayInverse()) {
             /*
@@ -719,6 +719,11 @@
     return mBufferInfo.mBuffer;
 }
 
+void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
+    GLConsumer::computeTransformMatrix(outMatrix, mBufferInfo.mBuffer, mBufferInfo.mCrop,
+                                       mBufferInfo.mTransform, filteringEnabled);
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index a685ea2..f0a30e3 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -127,6 +127,10 @@
 
     PixelFormat getPixelFormat() const;
 
+    // Computes the transform matrix using the setFilteringEnabled to determine whether the
+    // transform matrix should be computed for use with bilinear filtering.
+    void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
+
     virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
 
     virtual bool getAutoRefresh() const = 0;
@@ -137,8 +141,6 @@
 
     virtual bool hasFrameUpdate() const = 0;
 
-    virtual void setFilteringEnabled(bool enabled) = 0;
-
     virtual status_t bindTextureImage() = 0;
     virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                                     nsecs_t expectedPresentTime) = 0;
@@ -151,9 +153,8 @@
         nsecs_t mDesiredPresentTime;
         std::shared_ptr<FenceTime> mFenceTime;
         sp<Fence> mFence;
-        float mTransformMatrix[16];
         uint32_t mTransform{0};
-        ui::Dataspace mDataspace;
+        ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
         Rect mCrop;
         uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
         Region mSurfaceDamage;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index a9e364a..5f79750 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -151,13 +151,6 @@
     return mQueueItems[0].mTimestamp <= expectedPresentTime;
 }
 
-// NOTE: SurfaceFlinger's definitions of "Current" and "Drawing" do not neatly map to BufferQueue's
-// These functions get the fields for the frame that is currently in SurfaceFlinger's Drawing state
-// so the functions start with "getDrawing". The data is retrieved from the BufferQueueConsumer's
-// current buffer so the consumer functions start with "getCurrent".
-//
-// This results in the rather confusing functions below.
-
 uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
     Mutex::Autolock lock(mQueueItemLock);
     uint64_t frameNumber = mQueueItems[0].mFrameNumber;
@@ -220,10 +213,6 @@
     return mQueuedFrames > 0;
 }
 
-void BufferQueueLayer::setFilteringEnabled(bool enabled) {
-    return mConsumer->setFilteringEnabled(enabled);
-}
-
 status_t BufferQueueLayer::bindTextureImage() {
     return mConsumer->bindTextureImage();
 }
@@ -532,7 +521,6 @@
     mBufferInfo.mDesiredPresentTime = mConsumer->getTimestamp();
     mBufferInfo.mFenceTime = mConsumer->getCurrentFenceTime();
     mBufferInfo.mFence = mConsumer->getCurrentFence();
-    mConsumer->getTransformMatrix(mBufferInfo.mTransformMatrix);
     mBufferInfo.mTransform = mConsumer->getCurrentTransform();
     mBufferInfo.mDataspace = translateDataspace(mConsumer->getCurrentDataSpace());
     mBufferInfo.mCrop = mConsumer->getCurrentCrop();
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 43eb3ea..9374741 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -46,7 +46,6 @@
 
     std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
 
-
     // If a buffer was replaced this frame, release the former buffer
     void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
 
@@ -65,7 +64,6 @@
     bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
 
 private:
-
     uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
@@ -75,8 +73,6 @@
 
     bool hasFrameUpdate() const override;
 
-    void setFilteringEnabled(bool enabled) override;
-
     status_t bindTextureImage() override;
     status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                             nsecs_t expectedPresentTime) override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index afbe228..509deaf 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -432,11 +432,6 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
-void BufferStateLayer::setFilteringEnabled(bool enabled) {
-    GLConsumer::computeTransformMatrix(mTransformMatrix.data(), mBufferInfo.mBuffer,
-                                       mBufferInfo.mCrop, mBufferInfo.mTransform, enabled);
-}
-
 status_t BufferStateLayer::bindTextureImage() {
     const State& s(getDrawingState());
     auto& engine(mFlinger->getRenderEngine());
@@ -545,10 +540,8 @@
         return;
     }
 
-    const State& s(getDrawingState());
-
-    compositionState.buffer = s.buffer;
-    compositionState.bufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
+    compositionState.buffer = mBufferInfo.mBuffer;
+    compositionState.bufferSlot = mBufferInfo.mBufferSlot;
     compositionState.acquireFence = mBufferInfo.mFence;
 
     mFrameNumber++;
@@ -641,8 +634,6 @@
     mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
     mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
     mBufferInfo.mFence = s.acquireFence;
-    std::copy(std::begin(mTransformMatrix), std::end(mTransformMatrix),
-              mBufferInfo.mTransformMatrix);
     mBufferInfo.mTransform = s.transform;
     mBufferInfo.mDataspace = translateDataspace(s.dataspace);
     mBufferInfo.mCrop = computeCrop(s);
@@ -653,6 +644,7 @@
     mBufferInfo.mPixelFormat =
             !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
     mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse;
+    mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
 }
 
 Rect BufferStateLayer::computeCrop(const State& s) {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 0b27d64..52063fc 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -19,7 +19,6 @@
 #include "BufferLayer.h"
 #include "Layer.h"
 
-#include <gui/GLConsumer.h>
 #include <renderengine/Image.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
@@ -108,7 +107,6 @@
     void gatherBufferInfo() override;
 
 private:
-
     uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
@@ -118,8 +116,6 @@
 
     bool hasFrameUpdate() const override;
 
-    void setFilteringEnabled(bool enabled) override;
-
     status_t bindTextureImage() override;
     status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                             nsecs_t expectedPresentTime) override;
@@ -141,8 +137,6 @@
 
     std::unique_ptr<renderengine::Image> mTextureImage;
 
-    std::array<float, 16> mTransformMatrix{IDENTITY_MATRIX};
-
     std::atomic<bool> mSidebandStreamChanged{false};
 
     mutable uint32_t mFrameNumber{0};
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index d40a38c..4a11303 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -30,8 +30,4 @@
     return false;
 }
 
-bool ContainerLayer::canReceiveInput() const {
-    return !isHiddenByPolicy();
-}
-
 } // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index f0fbb61..c3624f6 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -31,8 +31,6 @@
     const char* getType() const override { return "ContainerLayer"; }
     bool isVisible() const override;
 
-    bool canReceiveInput() const override;
-
     bool isCreatedFromMainThread() const override { return true; }
 };
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8f6672c..bed33a7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1994,7 +1994,7 @@
 }
 
 bool Layer::canReceiveInput() const {
-    return isVisible();
+    return !isHiddenByPolicy();
 }
 
 compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 04e902b..6be88f8 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -51,7 +51,6 @@
     const Offsets defaultOffsets = getDefaultOffsets(thresholdForNextVsync);
     const Offsets highFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
 
-    mOffsets.insert({RefreshRateType::POWER_SAVING, defaultOffsets});
     mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
     mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
 }
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 9d47749..2fd100f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -41,10 +41,9 @@
  */
 class RefreshRateConfigs {
 public:
-    // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest
-    // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance
+    // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance
     // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
-    enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
+    enum class RefreshRateType { DEFAULT, PERFORMANCE };
 
     struct RefreshRate {
         // This config ID corresponds to the position of the config in the vector that is stored
@@ -54,26 +53,57 @@
         std::string name;
         // Refresh rate in frames per second, rounded to the nearest integer.
         uint32_t fps = 0;
-        // config Id (returned from HWC2::Display::Config::getId())
-        hwc2_config_t id;
+        // Vsync period in nanoseconds.
+        nsecs_t vsyncPeriod;
+        // Hwc config Id (returned from HWC2::Display::Config::getId())
+        hwc2_config_t hwcId;
     };
 
+    // Returns true if this device is doing refresh rate switching. This won't change at runtime.
+    bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; }
+
+    // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
+    // from multiple threads. This can only be called if refreshRateSwitching() returns true.
     // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
     // baking them in.
-    const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const {
-        return mRefreshRates;
-    }
-    std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const {
-        const auto& refreshRate = mRefreshRates.find(type);
-        if (refreshRate != mRefreshRates.end()) {
-            return refreshRate->second;
-        }
-        return nullptr;
+    const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const {
+        LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
+        return mRefreshRateMap;
     }
 
-    RefreshRateType getRefreshRateType(hwc2_config_t id) const {
-        for (const auto& [type, refreshRate] : mRefreshRates) {
-            if (refreshRate->id == id) {
+    const RefreshRate& getRefreshRateFromType(RefreshRateType type) const {
+        if (!mRefreshRateSwitchingSupported) {
+            return getCurrentRefreshRate().second;
+        } else {
+            auto refreshRate = mRefreshRateMap.find(type);
+            LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
+            return refreshRate->second;
+        }
+    }
+
+    std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const {
+        int currentConfig = mCurrentConfig;
+        if (mRefreshRateSwitchingSupported) {
+            for (const auto& [type, refresh] : mRefreshRateMap) {
+                if (refresh.configId == currentConfig) {
+                    return {type, refresh};
+                }
+            }
+            LOG_ALWAYS_FATAL();
+        }
+        return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
+    }
+
+    const RefreshRate& getRefreshRateFromConfigId(int configId) const {
+        LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
+        return mRefreshRates[configId];
+    }
+
+    RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
+        if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
+
+        for (const auto& [type, refreshRate] : mRefreshRateMap) {
+            if (refreshRate.hwcId == hwcId) {
                 return type;
             }
         }
@@ -81,64 +111,102 @@
         return RefreshRateType::DEFAULT;
     }
 
-    void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
-        mRefreshRates.clear();
+    void setCurrentConfig(int config) {
+        LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
+        mCurrentConfig = config;
+    }
 
-        // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
-        mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
-                              std::make_shared<RefreshRate>(
-                                      RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0,
-                                                  HWC2_SCREEN_OFF_CONFIG_ID}));
+    struct InputConfig {
+        hwc2_config_t hwcId = 0;
+        nsecs_t vsyncPeriod = 0;
+    };
 
-        if (configs.size() < 1) {
-            ALOGE("Device does not have valid configs. Config size is 0.");
-            return;
+    RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+                       int currentConfig) {
+        init(refreshRateSwitching, configs, currentConfig);
+    }
+
+    RefreshRateConfigs(bool refreshRateSwitching,
+                       const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+                       int currentConfig) {
+        std::vector<InputConfig> inputConfigs;
+        for (const auto& config : configs) {
+            inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
         }
-
-        // Create a map between config index and vsync period. This is all the info we need
-        // from the configs.
-        std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod;
-        for (int i = 0; i < configs.size(); ++i) {
-            configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod());
-        }
-
-        std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(),
-                  [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) {
-                      return a.second > b.second;
-                  });
-
-        // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT.
-        nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
-        if (vsyncPeriod != 0) {
-            const float fps = 1e9 / vsyncPeriod;
-            const int configId = configIdToVsyncPeriod[0].first;
-            mRefreshRates.emplace(RefreshRateType::DEFAULT,
-                                  std::make_shared<RefreshRate>(
-                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
-                                                      static_cast<uint32_t>(fps),
-                                                      configs.at(configId)->getId()}));
-        }
-
-        if (configs.size() < 2) {
-            return;
-        }
-
-        // When the configs are ordered by the resync rate. We assume that the second one is
-        // PERFORMANCE, eg. the higher rate.
-        vsyncPeriod = configIdToVsyncPeriod[1].second;
-        if (vsyncPeriod != 0) {
-            const float fps = 1e9 / vsyncPeriod;
-            const int configId = configIdToVsyncPeriod[1].first;
-            mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
-                                  std::make_shared<RefreshRate>(
-                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
-                                                      static_cast<uint32_t>(fps),
-                                                      configs.at(configId)->getId()}));
-        }
+        init(refreshRateSwitching, inputConfigs, currentConfig);
     }
 
 private:
-    std::map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
+    void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+              int currentConfig) {
+        mRefreshRateSwitchingSupported = refreshRateSwitching;
+        LOG_ALWAYS_FATAL_IF(configs.empty());
+        LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
+        mCurrentConfig = currentConfig;
+
+        auto buildRefreshRate = [&](int configId) -> RefreshRate {
+            const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
+            const float fps = 1e9 / vsyncPeriod;
+            return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
+                    vsyncPeriod, configs[configId].hwcId};
+        };
+
+        for (int i = 0; i < configs.size(); ++i) {
+            mRefreshRates.push_back(buildRefreshRate(i));
+        }
+
+        if (!mRefreshRateSwitchingSupported) return;
+
+        auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
+            if (configs.size() < 2) {
+                return {};
+            }
+
+            std::vector<const RefreshRate*> sortedRefreshRates;
+            for (const auto& refreshRate : mRefreshRates) {
+                sortedRefreshRates.push_back(&refreshRate);
+            }
+            std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
+                      [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
+                          return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+                      });
+
+            // When the configs are ordered by the resync rate, we assume that
+            // the first one is DEFAULT and the second one is PERFORMANCE,
+            // i.e. the higher rate.
+            if (sortedRefreshRates[0]->vsyncPeriod == 0 ||
+                sortedRefreshRates[1]->vsyncPeriod == 0) {
+                return {};
+            }
+
+            return std::pair<int, int>(sortedRefreshRates[0]->configId,
+                                       sortedRefreshRates[1]->configId);
+        };
+
+        auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
+        if (!defaultAndPerfConfigs) {
+            mRefreshRateSwitchingSupported = false;
+            return;
+        }
+
+        mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
+        mRefreshRateMap[RefreshRateType::PERFORMANCE] =
+                mRefreshRates[defaultAndPerfConfigs->second];
+    }
+
+    // Whether this device is doing refresh rate switching or not. This must not change after this
+    // object is initialized.
+    bool mRefreshRateSwitchingSupported;
+    // The list of refresh rates, indexed by display config ID. This must not change after this
+    // object is initialized.
+    std::vector<RefreshRate> mRefreshRates;
+    // The mapping of refresh rate type to RefreshRate. This must not change after this object is
+    // initialized.
+    std::map<RefreshRateType, RefreshRate> mRefreshRateMap;
+    // The ID of the current config. This will change at runtime. This is set by SurfaceFlinger on
+    // the main thread, and read by the Scheduler (and other objects) on other threads, so it's
+    // atomic.
+    std::atomic<int> mCurrentConfig;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 1f097db..8afc93e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -41,21 +41,18 @@
     static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
 
 public:
-    RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats)
-          : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats) {}
+    RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
+                     int currentConfigMode, int currentPowerMode)
+          : mRefreshRateConfigs(refreshRateConfigs),
+            mTimeStats(timeStats),
+            mCurrentConfigMode(currentConfigMode),
+            mCurrentPowerMode(currentPowerMode) {}
 
-    // Sets power mode. We only collect the information when the power mode is not
-    // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based
-    // on config mode.
+    // Sets power mode.
     void setPowerMode(int mode) {
         if (mCurrentPowerMode == mode) {
             return;
         }
-        // If power mode is normal, the time is going to be recorded under config modes.
-        if (mode == HWC_POWER_MODE_NORMAL) {
-            mCurrentPowerMode = mode;
-            return;
-        }
         flushTime();
         mCurrentPowerMode = mode;
     }
@@ -79,16 +76,15 @@
         flushTime();
 
         std::unordered_map<std::string, int64_t> totalTime;
-        for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
-            int64_t totalTimeForConfig = 0;
-            if (!config) {
-                continue;
-            }
-            if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
-                totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
-            }
-            totalTime[config->name] = totalTimeForConfig;
+        // Multiple configs may map to the same name, e.g. "60fps". Add the
+        // times for such configs together.
+        for (const auto& [config, time] : mConfigModesTotalTime) {
+            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0;
         }
+        for (const auto& [config, time] : mConfigModesTotalTime) {
+            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time;
+        }
+        totalTime["ScreenOff"] = mScreenOffTime;
         return totalTime;
     }
 
@@ -104,32 +100,26 @@
     }
 
 private:
-    void flushTime() {
-        // Normal power mode is counted under different config modes.
-        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
-            flushTimeForMode(mCurrentConfigMode);
-        } else {
-            flushTimeForMode(SCREEN_OFF_CONFIG_ID);
-        }
-    }
-
     // Calculates the time that passed in ms between the last time we recorded time and the time
     // this method was called.
-    void flushTimeForMode(int mode) {
+    void flushTime() {
         nsecs_t currentTime = systemTime();
         nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
         int64_t timeElapsedMs = ns2ms(timeElapsed);
         mPreviousRecordedTime = currentTime;
 
-        mConfigModesTotalTime[mode] += timeElapsedMs;
-        for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
-            if (!config) {
-                continue;
+        uint32_t fps = 0;
+        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+            // Normal power mode is counted under different config modes.
+            if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) {
+                mConfigModesTotalTime[mCurrentConfigMode] = 0;
             }
-            if (config->configId == mode) {
-                mTimeStats.recordRefreshRate(config->fps, timeElapsed);
-            }
+            mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
+            fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps;
+        } else {
+            mScreenOffTime += timeElapsedMs;
         }
+        mTimeStats.recordRefreshRate(fps, timeElapsed);
     }
 
     // Formats the time in milliseconds into easy to read format.
@@ -149,10 +139,11 @@
     // Aggregate refresh rate statistics for telemetry.
     TimeStats& mTimeStats;
 
-    int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID;
-    int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+    int mCurrentConfigMode;
+    int32_t mCurrentPowerMode;
 
-    std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+    std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime;
+    int64_t mScreenOffTime = 0;
 
     nsecs_t mPreviousRecordedTime = systemTime();
 };
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index e2a880a..add56fc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -118,20 +118,18 @@
 
 Scheduler::ConnectionHandle Scheduler::createConnection(
         const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
-        ResyncCallback resyncCallback,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
     auto eventThread = makeEventThread(connectionName, phaseOffsetNs, offsetThresholdForNextVsync,
                                        std::move(interceptCallback));
-    return createConnection(std::move(eventThread), std::move(resyncCallback));
+    return createConnection(std::move(eventThread));
 }
 
-Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread,
-                                                        ResyncCallback&& resyncCallback) {
+Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
     const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
     ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
 
-    auto connection = createConnectionInternal(eventThread.get(), std::move(resyncCallback),
-                                               ISurfaceComposer::eConfigChangedSuppress);
+    auto connection =
+            createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
 
     mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
     return handle;
@@ -148,17 +146,14 @@
 }
 
 sp<EventThreadConnection> Scheduler::createConnectionInternal(
-        EventThread* eventThread, ResyncCallback&& resyncCallback,
-        ISurfaceComposer::ConfigChanged configChanged) {
-    return eventThread->createEventConnection(std::move(resyncCallback), configChanged);
+        EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
+    return eventThread->createEventConnection([&] { resync(); }, configChanged);
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
-        ConnectionHandle handle, ResyncCallback resyncCallback,
-        ISurfaceComposer::ConfigChanged configChanged) {
+        ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
     RETURN_IF_INVALID_HANDLE(handle, nullptr);
-    return createConnectionInternal(mConnections[handle].thread.get(), std::move(resyncCallback),
-                                    configChanged);
+    return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
 }
 
 EventThread* Scheduler::getEventThread(ConnectionHandle handle) {
@@ -248,23 +243,15 @@
     setVsyncPeriod(period);
 }
 
-ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
-    std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
-    return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
-        if (const auto vsync = ptr.lock()) {
-            vsync->resync(getVsyncPeriod);
-        }
-    };
-}
-
-void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
-    static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
+void Scheduler::resync() {
+    static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
 
     const nsecs_t now = systemTime();
-    const nsecs_t last = lastResyncTime.exchange(now);
+    const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
+        resyncToHardwareVsync(false,
+                              mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
     }
 }
 
@@ -314,15 +301,19 @@
 
 std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
         std::string const& name, int windowType) {
-    RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER)
-            ? RefreshRateType::DEFAULT
-            : RefreshRateType::PERFORMANCE;
-
-    const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
-    const uint32_t performanceFps = (refreshRate) ? refreshRate->fps : 0;
-
-    const auto defaultRefreshRate = mRefreshRateConfigs.getRefreshRate(RefreshRateType::DEFAULT);
-    const uint32_t defaultFps = (defaultRefreshRate) ? defaultRefreshRate->fps : 0;
+    uint32_t defaultFps, performanceFps;
+    if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
+        defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
+        performanceFps =
+                mRefreshRateConfigs
+                        .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER)
+                                                        ? RefreshRateType::DEFAULT
+                                                        : RefreshRateType::PERFORMANCE)
+                        .fps;
+    } else {
+        defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
+        performanceFps = defaultFps;
+    }
     return mLayerHistory.createLayer(name, defaultFps, performanceFps);
 }
 
@@ -368,16 +359,6 @@
     mChangeRefreshRateCallback = std::move(callback);
 }
 
-void Scheduler::setGetCurrentRefreshRateTypeCallback(GetCurrentRefreshRateTypeCallback&& callback) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mGetCurrentRefreshRateTypeCallback = std::move(callback);
-}
-
-void Scheduler::setGetVsyncPeriodCallback(GetVsyncPeriod&& callback) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mGetVsyncPeriod = std::move(callback);
-}
-
 void Scheduler::resetIdleTimer() {
     if (mIdleTimer) {
         mIdleTimer->reset();
@@ -416,16 +397,13 @@
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
     ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
 
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (!mGetCurrentRefreshRateTypeCallback || !mGetVsyncPeriod) return;
-
-    const auto type = mGetCurrentRefreshRateTypeCallback();
-    if (state == TimerState::Reset && type == RefreshRateType::PERFORMANCE) {
+    const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+    if (state == TimerState::Reset && refreshRate.first == RefreshRateType::PERFORMANCE) {
         // If we're not in performance mode then the kernel timer shouldn't do
         // anything, as the refresh rate during DPU power collapse will be the
         // same.
-        resyncToHardwareVsync(true /* makeAvailable */, mGetVsyncPeriod());
-    } else if (state == TimerState::Expired && type != RefreshRateType::PERFORMANCE) {
+        resyncToHardwareVsync(true /* makeAvailable */, refreshRate.second.vsyncPeriod);
+    } else if (state == TimerState::Expired && refreshRate.first != RefreshRateType::PERFORMANCE) {
         // Disable HW VSYNC if the timer expired, as we don't need it enabled if
         // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
         // need to update the DispSync model anyway.
@@ -485,6 +463,10 @@
 }
 
 Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
+    if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
+        return RefreshRateType::DEFAULT;
+    }
+
     // HDR content is not supported on PERFORMANCE mode
     if (mForceHDRContentToDefaultRefreshRate && mFeatures.isHDRContent) {
         return RefreshRateType::DEFAULT;
@@ -514,16 +496,11 @@
     // Content detection is on, find the appropriate refresh rate with minimal error
     // TODO(b/139751853): Scan allowed refresh rates only (SurfaceFlinger::mAllowedDisplayConfigs)
     const float rate = static_cast<float>(mFeatures.contentRefreshRate);
-    auto begin = mRefreshRateConfigs.getRefreshRates().cbegin();
-
-    // Skip POWER_SAVING config as it is not a real config
-    if (begin->first == RefreshRateType::POWER_SAVING) {
-        ++begin;
-    }
-    auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRates().cend(),
+    auto iter = min_element(mRefreshRateConfigs.getRefreshRateMap().cbegin(),
+                            mRefreshRateConfigs.getRefreshRateMap().cend(),
                             [rate](const auto& lhs, const auto& rhs) -> bool {
-                                return std::abs(lhs.second->fps - rate) <
-                                        std::abs(rhs.second->fps - rate);
+                                return std::abs(lhs.second.fps - rate) <
+                                        std::abs(rhs.second.fps - rate);
                             });
     RefreshRateType currRefreshRateType = iter->first;
 
@@ -531,10 +508,10 @@
     // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
     // align well with both
     constexpr float MARGIN = 0.05f;
-    float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps / rate;
+    float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps / rate;
     if (std::abs(std::round(ratio) - ratio) > MARGIN) {
-        while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
-            ratio = iter->second->fps / rate;
+        while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
+            ratio = iter->second.fps / rate;
 
             if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
                 currRefreshRateType = iter->first;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 34e527c..0c8c335 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -43,9 +43,7 @@
     using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
     using ConfigEvent = scheduler::RefreshRateConfigEvent;
 
-    using GetCurrentRefreshRateTypeCallback = std::function<RefreshRateType()>;
     using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
-    using GetVsyncPeriod = std::function<nsecs_t()>;
 
     // Indicates whether to start the transaction early, or at vsync time.
     enum class TransactionStart { EARLY, NORMAL };
@@ -59,10 +57,10 @@
 
     using ConnectionHandle = scheduler::ConnectionHandle;
     ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
-                                      nsecs_t offsetThresholdForNextVsync, ResyncCallback,
+                                      nsecs_t offsetThresholdForNextVsync,
                                       impl::EventThread::InterceptVSyncsCallback);
 
-    sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle, ResyncCallback,
+    sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
                                                              ISurfaceComposer::ConfigChanged);
 
     EventThread* getEventThread(ConnectionHandle);
@@ -88,7 +86,7 @@
     // no-op.
     // The period is the vsync period from the current display configuration.
     void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
-    ResyncCallback makeResyncCallback(GetVsyncPeriod&&);
+    void resync();
 
     // Passes a vsync sample to DispSync. periodFlushed will be true if
     // DispSync detected that the vsync period changed, and false otherwise.
@@ -113,9 +111,6 @@
     // Called by Scheduler to change refresh rate.
     void setChangeRefreshRateCallback(ChangeRefreshRateCallback&&);
 
-    void setGetCurrentRefreshRateTypeCallback(GetCurrentRefreshRateTypeCallback&&);
-    void setGetVsyncPeriodCallback(GetVsyncPeriod&&);
-
     bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
     void resetIdleTimer();
 
@@ -149,8 +144,8 @@
                                                  impl::EventThread::InterceptVSyncsCallback&&);
 
     // Create a connection on the given EventThread and forward the resync callback.
-    ConnectionHandle createConnection(std::unique_ptr<EventThread>, ResyncCallback&&);
-    sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
+    ConnectionHandle createConnection(std::unique_ptr<EventThread>);
+    sp<EventThreadConnection> createConnectionInternal(EventThread*,
                                                        ISurfaceComposer::ConfigChanged);
 
     // Update feature state machine to given state when corresponding timer resets or expires.
@@ -182,17 +177,7 @@
     bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
     bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
 
-    // Stores per-display state about VSYNC.
-    struct VsyncState {
-        explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {}
-
-        void resync(const GetVsyncPeriod&);
-
-        Scheduler& scheduler;
-        std::atomic<nsecs_t> lastResyncTime = 0;
-    };
-
-    const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)};
+    std::atomic<nsecs_t> mLastResyncTime = 0;
 
     std::unique_ptr<DispSync> mPrimaryDispSync;
     std::unique_ptr<EventControlThread> mEventControlThread;
@@ -211,9 +196,7 @@
     std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
 
     std::mutex mCallbackLock;
-    GetCurrentRefreshRateTypeCallback mGetCurrentRefreshRateTypeCallback GUARDED_BY(mCallbackLock);
     ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
-    GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock);
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index ab0c0ff..3bb3a6f 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -40,12 +40,6 @@
 
 using namespace std::chrono_literals;
 
-// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently
-// the config is not visible to SF, and is completely maintained by HWC. However, we would
-// still like to keep track of time when the device is in this config.
-static constexpr int SCREEN_OFF_CONFIG_ID = -1;
-static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff;
-
 // This number is used when we try to determine how long do we keep layer information around
 // before we remove it. It is also used to determine how long the layer stays relevant.
 // This time period captures infrequent updates when playing YouTube video with static image,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4361a94..c01d3aa 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -340,11 +340,6 @@
     mPropagateBackpressure = !atoi(value);
     ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation");
 
-    property_get("debug.sf.enable_gl_backpressure", value, "0");
-    mPropagateBackpressureClientComposition = atoi(value);
-    ALOGI_IF(mPropagateBackpressureClientComposition,
-             "Enabling backpressure propagation for Client Composition");
-
     property_get("debug.sf.enable_hwc_vds", value, "0");
     mUseHwcVirtualDisplays = atoi(value);
     ALOGI_IF(mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
@@ -549,14 +544,16 @@
         readPersistentProperties();
         mBootStage = BootStage::FINISHED;
 
-        // set the refresh rate according to the policy
-        const auto& performanceRefreshRate =
-                mRefreshRateConfigs.getRefreshRate(RefreshRateType::PERFORMANCE);
+        if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+            // set the refresh rate according to the policy
+            const auto& performanceRefreshRate =
+                    mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE);
 
-        if (performanceRefreshRate && isDisplayConfigAllowed(performanceRefreshRate->configId)) {
-            setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
-        } else {
-            setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
+            if (isDisplayConfigAllowed(performanceRefreshRate.configId)) {
+                setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
+            } else {
+                setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
+            }
         }
     }));
 }
@@ -595,37 +592,9 @@
 void SurfaceFlinger::init() {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
-
     ALOGI("Phase offset: %" PRId64 " ns", mPhaseOffsets->getCurrentAppOffset());
 
     Mutex::Autolock _l(mStateLock);
-    // start the EventThread
-    mScheduler =
-            getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
-                                         mRefreshRateConfigs);
-    auto resyncCallback =
-            mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
-    mAppConnectionHandle =
-            mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
-                                         resyncCallback,
-                                         impl::EventThread::InterceptVSyncsCallback());
-    mSfConnectionHandle =
-            mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
-                                         resyncCallback, [this](nsecs_t timestamp) {
-                                             mInterceptor->saveVSyncEvent(timestamp);
-                                         });
-
-    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
-
-    mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
-                            mPhaseOffsets->getCurrentOffsets());
-
-    mRegionSamplingThread =
-            new RegionSamplingThread(*this, *mScheduler,
-                                     RegionSamplingThread::EnvironmentTimingTunables());
 
     // Get a RenderEngine for the given display / config (can't fail)
     int32_t renderEngineFeature = 0;
@@ -700,37 +669,6 @@
         ALOGE("Run StartPropertySetThread failed!");
     }
 
-    mScheduler->setChangeRefreshRateCallback(
-            [this](RefreshRateType type, Scheduler::ConfigEvent event) {
-                Mutex::Autolock lock(mStateLock);
-                setRefreshRateTo(type, event);
-            });
-    mScheduler->setGetCurrentRefreshRateTypeCallback([this] {
-        Mutex::Autolock lock(mStateLock);
-        const auto display = getDefaultDisplayDeviceLocked();
-        if (!display) {
-            // If we don't have a default display the fallback to the default
-            // refresh rate type
-            return RefreshRateType::DEFAULT;
-        }
-
-        const int configId = display->getActiveConfig();
-        for (const auto& [type, refresh] : mRefreshRateConfigs.getRefreshRates()) {
-            if (refresh && refresh->configId == configId) {
-                return type;
-            }
-        }
-        // This should never happen, but just gracefully fallback to default.
-        return RefreshRateType::DEFAULT;
-    });
-    mScheduler->setGetVsyncPeriodCallback([this] {
-        Mutex::Autolock lock(mStateLock);
-        return getVsyncPeriod();
-    });
-
-    mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId()));
-    mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
-
     ALOGV("Done initializing");
 }
 
@@ -884,7 +822,8 @@
         info.xdpi = xdpi;
         info.ydpi = ydpi;
         info.fps = 1e9 / hwConfig->getVsyncPeriod();
-        const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId());
+        const auto refreshRateType =
+                mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId());
         const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
         info.appVsyncOffset = offset.late.app;
 
@@ -983,7 +922,8 @@
     }
 
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    mRefreshRateStats.setConfigMode(mUpcomingActiveConfig.configId);
+    mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId);
+    mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
 
     display->setActiveConfig(mUpcomingActiveConfig.configId);
 
@@ -1259,9 +1199,6 @@
             return;
         }
 
-        auto resyncCallback =
-                mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
         // TODO(b/128863962): Part of the Injector should be refactored, so that it
         // can be passed to Scheduler.
         if (enable) {
@@ -1273,11 +1210,11 @@
                                            impl::EventThread::InterceptVSyncsCallback(),
                                            "injEventThread");
             }
-            mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback));
+            mEventQueue->setEventThread(mInjectorEventThread.get(), [&] { mScheduler->resync(); });
         } else {
             ALOGV("VSync Injections disabled");
             mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle),
-                                        std::move(resyncCallback));
+                                        [&] { mScheduler->resync(); });
         }
 
         mInjectVSyncs = enable;
@@ -1388,16 +1325,10 @@
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
         ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
-    auto resyncCallback = mScheduler->makeResyncCallback([this] {
-        Mutex::Autolock lock(mStateLock);
-        return getVsyncPeriod();
-    });
-
     const auto& handle =
             vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
 
-    return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback),
-                                                    configChanged);
+    return mScheduler->createDisplayEventConnection(handle, configChanged);
 }
 
 // ----------------------------------------------------------------------------
@@ -1494,13 +1425,8 @@
     ATRACE_CALL();
 
     // Don't do any updating if the current fps is the same as the new one.
-    const auto& refreshRateConfig = mRefreshRateConfigs.getRefreshRate(refreshRate);
-    if (!refreshRateConfig) {
-        ALOGV("Skipping refresh rate change request for unsupported rate.");
-        return;
-    }
-
-    const int desiredConfigId = refreshRateConfig->configId;
+    const auto& refreshRateConfig = mRefreshRateConfigs->getRefreshRateFromType(refreshRate);
+    const int desiredConfigId = refreshRateConfig.configId;
 
     if (!isDisplayConfigAllowed(desiredConfigId)) {
         ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
@@ -1721,9 +1647,9 @@
                 break;
             }
 
-            if (frameMissed && mPropagateBackpressure) {
-                if ((hwcFrameMissed && !gpuFrameMissed) ||
-                    mPropagateBackpressureClientComposition) {
+            // For now, only propagate backpressure when missing a hwc frame.
+            if (hwcFrameMissed && !gpuFrameMissed) {
+                if (mPropagateBackpressure) {
                     signalLayerUpdate();
                     break;
                 }
@@ -2202,6 +2128,9 @@
         if (event.connection == HWC2::Connection::Connected) {
             if (!mPhysicalDisplayTokens.count(info->id)) {
                 ALOGV("Creating display %s", to_string(info->id).c_str());
+                if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+                    initScheduler(info->id);
+                }
                 mPhysicalDisplayTokens[info->id] = new BBinder();
                 DisplayDeviceState state;
                 state.displayId = info->id;
@@ -2656,6 +2585,55 @@
     mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
+void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
+    if (mScheduler) {
+        // In practice it's not allowed to hotplug in/out the primary display once it's been
+        // connected during startup, but some tests do it, so just warn and return.
+        ALOGW("Can't re-init scheduler");
+        return;
+    }
+
+    int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId);
+    mRefreshRateConfigs =
+            std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false),
+                                                            getHwComposer().getConfigs(
+                                                                    primaryDisplayId),
+                                                            currentConfig);
+    mRefreshRateStats =
+            std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats,
+                                                          currentConfig, HWC_POWER_MODE_OFF);
+    mRefreshRateStats->setConfigMode(currentConfig);
+
+    // start the EventThread
+    mScheduler =
+            getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
+                                         *mRefreshRateConfigs);
+    mAppConnectionHandle =
+            mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
+                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
+                                         impl::EventThread::InterceptVSyncsCallback());
+    mSfConnectionHandle =
+            mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
+                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
+                                         [this](nsecs_t timestamp) {
+                                             mInterceptor->saveVSyncEvent(timestamp);
+                                         });
+
+    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+    mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
+                            mPhaseOffsets->getCurrentOffsets());
+
+    mRegionSamplingThread =
+            new RegionSamplingThread(*this, *mScheduler,
+                                     RegionSamplingThread::EnvironmentTimingTunables());
+
+    mScheduler->setChangeRefreshRateCallback(
+            [this](RefreshRateType type, Scheduler::ConfigEvent event) {
+                Mutex::Autolock lock(mStateLock);
+                setRefreshRateTo(type, event);
+            });
+}
+
 void SurfaceFlinger::commitTransaction()
 {
     withTracingLock([&]() {
@@ -4000,7 +3978,7 @@
 
     if (display->isPrimary()) {
         mTimeStats->setPowerMode(mode);
-        mRefreshRateStats.setPowerMode(mode);
+        mRefreshRateStats->setPowerMode(mode);
         mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL);
     }
 
@@ -4164,9 +4142,11 @@
 
 void SurfaceFlinger::dumpVSync(std::string& result) const {
     mScheduler->dump(result);
+    StringAppendF(&result, "+  Refresh rate switching: %s\n",
+                  mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off");
     StringAppendF(&result, "+  Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
 
-    mRefreshRateStats.dump(result);
+    mRefreshRateStats->dump(result);
     result.append("\n");
 
     mPhaseOffsets->dump(result);
@@ -4175,10 +4155,9 @@
                   dispSyncPresentTimeOffset, getVsyncPeriod());
 
     StringAppendF(&result, "Allowed Display Configs: ");
-    for (const auto& [type, rate] : mRefreshRateConfigs.getRefreshRates()) {
-        if (rate && isDisplayConfigAllowed(rate->configId)) {
-            StringAppendF(&result, "%" PRIu32 " Hz, ", rate->fps);
-        }
+    for (int32_t configId : mAllowedDisplayConfigs) {
+        StringAppendF(&result, "%" PRIu32 " Hz, ",
+                      mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps);
     }
     StringAppendF(&result, "(config override by backdoor: %s)\n\n",
                   mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
@@ -5020,7 +4999,8 @@
             case 1034: {
                 // TODO(b/129297325): expose this via developer menu option
                 n = data.readInt32();
-                if (n && !mRefreshRateOverlay) {
+                if (n && !mRefreshRateOverlay &&
+                    mRefreshRateConfigs->refreshRateSwitchingSupported()) {
                     RefreshRateType type;
                     {
                         std::lock_guard<std::mutex> lock(mActiveConfigLock);
@@ -5606,25 +5586,6 @@
     }
 }
 
-void SurfaceFlinger::setPreferredDisplayConfig() {
-    const auto& type = mScheduler->getPreferredRefreshRateType();
-    const auto& config = mRefreshRateConfigs.getRefreshRate(type);
-    if (config && isDisplayConfigAllowed(config->configId)) {
-        ALOGV("switching to Scheduler preferred config %d", config->configId);
-        setDesiredActiveConfig({type, config->configId, Scheduler::ConfigEvent::Changed});
-    } else {
-        // Set the highest allowed config by iterating backwards on available refresh rates
-        const auto& refreshRates = mRefreshRateConfigs.getRefreshRates();
-        for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
-            if (iter->second && isDisplayConfigAllowed(iter->second->configId)) {
-                ALOGV("switching to allowed config %d", iter->second->configId);
-                setDesiredActiveConfig({iter->first, iter->second->configId,
-                        Scheduler::ConfigEvent::Changed});
-            }
-        }
-    }
-}
-
 void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
                                                       const std::vector<int32_t>& allowedConfigs) {
     if (!display->isPrimary()) {
@@ -5646,7 +5607,29 @@
     mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
                                 display->getActiveConfig());
 
-    setPreferredDisplayConfig();
+    if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+        const auto& type = mScheduler->getPreferredRefreshRateType();
+        const auto& config = mRefreshRateConfigs->getRefreshRateFromType(type);
+        if (isDisplayConfigAllowed(config.configId)) {
+            ALOGV("switching to Scheduler preferred config %d", config.configId);
+            setDesiredActiveConfig({type, config.configId, Scheduler::ConfigEvent::Changed});
+        } else {
+            // Set the highest allowed config by iterating backwards on available refresh rates
+            const auto& refreshRates = mRefreshRateConfigs->getRefreshRateMap();
+            for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
+                if (isDisplayConfigAllowed(iter->second.configId)) {
+                    ALOGV("switching to allowed config %d", iter->second.configId);
+                    setDesiredActiveConfig(
+                            {iter->first, iter->second.configId, Scheduler::ConfigEvent::Changed});
+                    break;
+                }
+            }
+        }
+    } else if (!allowedConfigs.empty()) {
+        ALOGV("switching to config %d", allowedConfigs[0]);
+        setDesiredActiveConfig(
+                {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed});
+    }
 }
 
 status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index beb43d0..aaa50f6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -529,9 +529,6 @@
     // called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
 
-    // Query the Scheduler or allowed display configs list for a matching config, and set it
-    void setPreferredDisplayConfig() REQUIRES(mStateLock);
-
     // called on the main thread in response to setAllowedDisplayConfigs()
     void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
                                           const std::vector<int32_t>& allowedConfigs)
@@ -554,6 +551,7 @@
     void executeInputWindowCommands();
     void setInputWindowsFinished();
     void updateCursorAsync();
+    void initScheduler(DisplayId primaryDisplayId);
 
     /* handlePageFlip - latch a new buffer if available and compute the dirty
      * region. Returns whether a new buffer has been latched, i.e., whether it
@@ -985,7 +983,6 @@
     volatile nsecs_t mDebugInTransaction = 0;
     bool mForceFullDamage = false;
     bool mPropagateBackpressure = true;
-    bool mPropagateBackpressureClientComposition = false;
     std::unique_ptr<SurfaceInterceptor> mInterceptor;
     SurfaceTracing mTracing{*this};
     bool mTracingEnabled = false;
@@ -1102,8 +1099,8 @@
     // Optional to defer construction until scheduler connections are created.
     std::optional<scheduler::VSyncModulator> mVSyncModulator;
 
-    scheduler::RefreshRateConfigs mRefreshRateConfigs;
-    scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats};
+    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
     std::atomic<nsecs_t> mExpectedPresentTime = 0;
 
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 768074a..b4716eb 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -226,6 +226,14 @@
     return static_cast<int64_t>(defaultValue);
 }
 
+bool refresh_rate_switching(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::refresh_rate_switching();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 int32_t set_idle_timer_ms(int32_t defaultValue) {
     auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
     if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 5f88322..e394cca 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -73,6 +73,8 @@
 int64_t color_space_agnostic_dataspace(
         android::hardware::graphics::common::V1_2::Dataspace defaultValue);
 
+bool refresh_rate_switching(bool defaultValue);
+
 int32_t set_idle_timer_ms(int32_t defaultValue);
 
 int32_t set_touch_timer_ms(int32_t defaultValue);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index b01fa81..3df8360 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -34,6 +34,14 @@
 
 namespace impl {
 
+TimeStats::TimeStats() {
+    // Temporarily enable TimeStats by default. Telemetry is disabled while
+    // we move onto statsd, so TimeStats is currently not exercised at all
+    // during testing.
+    // TODO: remove this.
+    enable();
+}
+
 void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
     ATRACE_CALL();
 
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 9ebc1ad..1313132 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -106,7 +106,7 @@
     };
 
 public:
-    TimeStats() = default;
+    TimeStats();
 
     void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
     bool isEnabled() override;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 56ab4e3..51b20cb 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -15,7 +15,7 @@
 module: "android.sysprop.SurfaceFlingerProperties"
 owner: Platform
 
-# The following two propertiess define (respectively):
+# The following two properties define (respectively):
 #
 # - The phase offset between hardware vsync and when apps are woken up by the
 #   Choreographer callback
@@ -301,9 +301,18 @@
     prop_name: "ro.surface_flinger.display_primary_white"
 }
 
-# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is
-# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower
-# refresh rate. Setting this property to 0 means there is no timer.
+# refreshRateSwitching indicates whether SurfaceFlinger should use refresh rate
+# switching on the device, e.g. to switch between 60 and 90 Hz. The settings
+# below that are related to refresh rate switching will only have an effect if
+# refresh_rate_switching is enabled.
+prop {
+    api_name: "refresh_rate_switching"
+    type: Boolean
+    scope: System
+    access: Readonly
+    prop_name: "ro.surface_flinger.refresh_rate_switching"
+}
+
 prop {
     api_name: "set_idle_timer_ms"
     type: Integer
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index b66e56e..2d52507 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -73,6 +73,10 @@
     enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
   }
   prop {
+    api_name: "refresh_rate_switching"
+    prop_name: "ro.surface_flinger.refresh_rate_switching"
+  }
+  prop {
     api_name: "running_without_sync_framework"
     prop_name: "ro.surface_flinger.running_without_sync_framework"
   }
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 5067fe8..f315a8a 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -23,7 +23,6 @@
 
 #include "DisplayHardware/HWC2.h"
 #include "Scheduler/RefreshRateConfigs.h"
-#include "mock/DisplayHardware/MockDisplay.h"
 
 using namespace std::chrono_literals;
 using testing::_;
@@ -50,9 +49,8 @@
         ASSERT_EQ(left.configId, right.configId);
         ASSERT_EQ(left.name, right.name);
         ASSERT_EQ(left.fps, right.fps);
+        ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod);
     }
-
-    RefreshRateConfigs mConfigs;
 };
 
 RefreshRateConfigsTest::RefreshRateConfigsTest() {
@@ -71,101 +69,39 @@
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
-    mConfigs.populate(displayConfigs);
-
-    // We always store a configuration for screen off.
-    const auto& rates = mConfigs.getRefreshRates();
-    ASSERT_EQ(1, rates.size());
-    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
-    ASSERT_NE(rates.end(), powerSavingRate);
-    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
-    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
-
-    RefreshRate expectedConfig =
-            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
-    assertRatesEqual(expectedConfig, *powerSavingRate->second);
-
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    assertRatesEqual(expectedConfig, *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
-    ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-
-    // Sanity check that getRefreshRate() does not modify the underlying configs.
-    ASSERT_EQ(1, mConfigs.getRefreshRates().size());
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+                                                 /*currentConfig=*/0);
+    ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
 }
 
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
-    auto display = new Hwc2::mock::Display();
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
-    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
-    config60.setVsyncPeriod(VSYNC_60);
-    displayConfigs.push_back(config60.build());
-    mConfigs.populate(displayConfigs);
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60},
+                                                         {HWC2_CONFIG_ID_90, VSYNC_90}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+                                                 /*currentConfig=*/0);
 
-    const auto& rates = mConfigs.getRefreshRates();
+    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+    const auto& rates = refreshRateConfigs->getRefreshRateMap();
     ASSERT_EQ(2, rates.size());
-    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
-    const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
-    ASSERT_NE(rates.end(), powerSavingRate);
-    ASSERT_NE(rates.end(), defaultRate);
-    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
-
-    RefreshRate expectedPowerSavingConfig =
-            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
-    assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
-    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
-    assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
-
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    assertRatesEqual(expectedPowerSavingConfig,
-                     *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
-
-    // Sanity check that getRefreshRate() does not modify the underlying configs.
-    ASSERT_EQ(2, mConfigs.getRefreshRates().size());
-}
-
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
-    auto display = new Hwc2::mock::Display();
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
-    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
-    config60.setVsyncPeriod(VSYNC_60);
-    displayConfigs.push_back(config60.build());
-    auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
-    config90.setVsyncPeriod(VSYNC_90);
-    displayConfigs.push_back(config90.build());
-    mConfigs.populate(displayConfigs);
-
-    const auto& rates = mConfigs.getRefreshRates();
-    ASSERT_EQ(3, rates.size());
-    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
     const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
     const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
-    ASSERT_NE(rates.end(), powerSavingRate);
     ASSERT_NE(rates.end(), defaultRate);
     ASSERT_NE(rates.end(), performanceRate);
 
-    RefreshRate expectedPowerSavingConfig =
-            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
-    assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
-    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
-    assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
-    RefreshRate expectedPerformanceConfig =
-            RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90};
-    assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
+    RefreshRate expectedDefaultConfig = {CONFIG_ID_60, "60fps", 60, VSYNC_60, HWC2_CONFIG_ID_60};
+    assertRatesEqual(expectedDefaultConfig, defaultRate->second);
+    RefreshRate expectedPerformanceConfig = {CONFIG_ID_90, "90fps", 90, VSYNC_90,
+                                             HWC2_CONFIG_ID_90};
+    assertRatesEqual(expectedPerformanceConfig, performanceRate->second);
 
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    assertRatesEqual(expectedPowerSavingConfig,
-                     *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+    assertRatesEqual(expectedDefaultConfig,
+                     refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT));
     assertRatesEqual(expectedPerformanceConfig,
-                     *mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+                     refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE));
 }
 } // namespace
 } // namespace scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 411ec61..cec0b32 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -22,7 +22,6 @@
 #include <thread>
 
 #include "Scheduler/RefreshRateStats.h"
-#include "mock/DisplayHardware/MockDisplay.h"
 #include "mock/MockTimeStats.h"
 
 using namespace std::chrono_literals;
@@ -42,9 +41,18 @@
     RefreshRateStatsTest();
     ~RefreshRateStatsTest();
 
+    void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
+        mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>(
+                /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0);
+        mRefreshRateStats =
+                std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
+                                                   /*currentConfig=*/0,
+                                                   /*currentPowerMode=*/HWC_POWER_MODE_OFF);
+    }
+
     mock::TimeStats mTimeStats;
-    RefreshRateConfigs mRefreshRateConfigs;
-    RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, mTimeStats};
+    std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<RefreshRateStats> mRefreshRateStats;
 };
 
 RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -63,63 +71,46 @@
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
-    mRefreshRateConfigs.populate(configs);
-
-    // There is one default config, so the refresh rates should have one item.
-    EXPECT_EQ(1, mRefreshRateStats.getTotalTimes().size());
-}
-
 TEST_F(RefreshRateStatsTest, oneConfigTest) {
-    auto display = new Hwc2::mock::Display();
-
-    auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
-    config.setVsyncPeriod(VSYNC_90);
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
-    configs.push_back(config.build());
-
-    mRefreshRateConfigs.populate(configs);
+    init({{CONFIG_ID_90, VSYNC_90}});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
 
-    std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
-    EXPECT_EQ(2, times.size());
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(1, times.size());
     EXPECT_NE(0u, times.count("ScreenOff"));
-    EXPECT_EQ(1u, times.count("90fps"));
-    EXPECT_EQ(0, times["90fps"]);
     // Setting up tests on mobile harness can be flaky with time passing, so testing for
     // exact time changes can result in flaxy numbers. To avoid that remember old
     // numbers to make sure the correct values are increasing in the next test.
     int screenOff = times["ScreenOff"];
-    int ninety = times["90fps"];
 
     // Screen is off by default.
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(0, times["90fps"]);
+    EXPECT_EQ(0u, times.count("90fps"));
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
-    EXPECT_LT(ninety, times["90fps"]);
+    ASSERT_EQ(1u, times.count("90fps"));
+    EXPECT_LT(0, times["90fps"]);
 
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE);
-    ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
     // does not update refresh rates that come from the config.
     EXPECT_LT(screenOff, times["ScreenOff"]);
@@ -127,93 +118,75 @@
 }
 
 TEST_F(RefreshRateStatsTest, twoConfigsTest) {
-    auto display = new Hwc2::mock::Display();
-
-    auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
-    config90.setVsyncPeriod(VSYNC_90);
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
-    configs.push_back(config90.build());
-
-    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
-    config60.setVsyncPeriod(VSYNC_60);
-    configs.push_back(config60.build());
-
-    mRefreshRateConfigs.populate(configs);
+    init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
 
-    std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
-    EXPECT_EQ(3, times.size());
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(1, times.size());
     EXPECT_NE(0u, times.count("ScreenOff"));
-    EXPECT_EQ(1u, times.count("60fps"));
-    EXPECT_EQ(0, times["60fps"]);
-    EXPECT_EQ(1u, times.count("90fps"));
-    EXPECT_EQ(0, times["90fps"]);
     // Setting up tests on mobile harness can be flaky with time passing, so testing for
     // exact time changes can result in flaxy numbers. To avoid that remember old
     // numbers to make sure the correct values are increasing in the next test.
     int screenOff = times["ScreenOff"];
-    int sixty = times["60fps"];
-    int ninety = times["90fps"];
 
     // Screen is off by default.
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(sixty, times["60fps"]);
-    EXPECT_EQ(ninety, times["90fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(sixty, times["60fps"]);
-    EXPECT_LT(ninety, times["90fps"]);
+    ASSERT_EQ(1u, times.count("90fps"));
+    EXPECT_LT(0, times["90fps"]);
 
     // When power mode is normal, time for configs updates.
-    mRefreshRateStats.setConfigMode(CONFIG_ID_60);
-    ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
-    EXPECT_LT(sixty, times["60fps"]);
+    ASSERT_EQ(1u, times.count("60fps"));
+    EXPECT_LT(0, times["60fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
     EXPECT_LT(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_60);
-    ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_LT(sixty, times["60fps"]);
 
     // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
     // does not update refresh rates that come from the config.
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE);
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    sixty = mRefreshRateStats->getTotalTimes()["60fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_60);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index ebcb9d8..bf53124 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -9,6 +9,7 @@
 
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
+#include "Scheduler/RefreshRateConfigs.h"
 #include "TestableScheduler.h"
 #include "mock/MockEventThread.h"
 
@@ -36,8 +37,8 @@
     SchedulerTest();
     ~SchedulerTest() override;
 
-    scheduler::RefreshRateConfigs mRefreshRateConfigs;
-    TestableScheduler mScheduler{mRefreshRateConfigs};
+    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<TestableScheduler> mScheduler;
 
     Scheduler::ConnectionHandle mConnectionHandle;
     mock::EventThread* mEventThread;
@@ -49,6 +50,13 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
+    std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+    mRefreshRateConfigs =
+            std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+                                                            /*currentConfig=*/0);
+
+    mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs);
+
     auto eventThread = std::make_unique<mock::EventThread>();
     mEventThread = eventThread.get();
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -60,7 +68,7 @@
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
             .WillRepeatedly(Return(mEventThreadConnection));
 
-    mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
+    mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
     EXPECT_TRUE(mConnectionHandle);
 }
 
@@ -80,61 +88,60 @@
 
     sp<IDisplayEventConnection> connection;
     ASSERT_NO_FATAL_FAILURE(
-            connection = mScheduler.createDisplayEventConnection(handle, ResyncCallback(),
-                                                                 ISurfaceComposer::
-                                                                         eConfigChangedSuppress));
+            connection = mScheduler->createDisplayEventConnection(handle,
+                                                                  ISurfaceComposer::
+                                                                          eConfigChangedSuppress));
     EXPECT_FALSE(connection);
-    EXPECT_FALSE(mScheduler.getEventThread(handle));
-    EXPECT_FALSE(mScheduler.getEventConnection(handle));
+    EXPECT_FALSE(mScheduler->getEventThread(handle));
+    EXPECT_FALSE(mScheduler->getEventConnection(handle));
 
     // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
     EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onScreenAcquired(handle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onScreenReleased(handle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
 
     std::string output;
     EXPECT_CALL(*mEventThread, dump(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.dump(handle, output));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
     EXPECT_TRUE(output.empty());
 
     EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.setPhaseOffset(handle, 10));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
 }
 
 TEST_F(SchedulerTest, validConnectionHandle) {
     sp<IDisplayEventConnection> connection;
     ASSERT_NO_FATAL_FAILURE(
-            connection =
-                    mScheduler.createDisplayEventConnection(mConnectionHandle, ResyncCallback(),
-                                                            ISurfaceComposer::
-                                                                    eConfigChangedSuppress));
+            connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
+                                                                  ISurfaceComposer::
+                                                                          eConfigChangedSuppress));
     ASSERT_EQ(mEventThreadConnection, connection);
 
-    EXPECT_TRUE(mScheduler.getEventThread(mConnectionHandle));
-    EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
+    EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle));
+    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
 
     EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
     ASSERT_NO_FATAL_FAILURE(
-            mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+            mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onScreenAcquired(mConnectionHandle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.onScreenReleased(mConnectionHandle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
 
     std::string output("dump");
     EXPECT_CALL(*mEventThread, dump(output)).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.dump(mConnectionHandle, output));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
     EXPECT_FALSE(output.empty());
 
     EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler.setPhaseOffset(mConnectionHandle, 10));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 780b608..ae72467 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -37,7 +37,7 @@
 
     // Used to inject mock event thread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
-        return Scheduler::createConnection(std::move(eventThread), ResyncCallback());
+        return Scheduler::createConnection(std::move(eventThread));
     }
 
     /* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 9536dd1..5e075b6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -32,6 +32,7 @@
 #include "Layer.h"
 #include "NativeWindowSurface.h"
 #include "Scheduler/MessageQueue.h"
+#include "Scheduler/RefreshRateConfigs.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerFactory.h"
@@ -194,9 +195,19 @@
                         std::unique_ptr<EventControlThread> eventControlThread,
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread) {
+        std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+        mFlinger->mRefreshRateConfigs =
+                std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false,
+                                                                configs, /*currentConfig=*/0);
+        mFlinger->mRefreshRateStats =
+                std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs,
+                                                              *mFlinger->mTimeStats,
+                                                              /*currentConfig=*/0,
+                                                              /*powerMode=*/HWC_POWER_MODE_OFF);
+
         mScheduler =
                 new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
-                                      mFlinger->mRefreshRateConfigs);
+                                      *mFlinger->mRefreshRateConfigs);
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index ffacbfe..dee2cae 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -209,6 +209,10 @@
     return distr(mRandomEngine);
 }
 
+TEST_F(TimeStatsTest, enabledByDefault) {
+    ASSERT_TRUE(mTimeStats->isEnabled());
+}
+
 TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
     ASSERT_TRUE(mTimeStats->isEnabled());
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 651c94d..83a5250 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -75,7 +75,7 @@
 
     header_libs: [
         "hwvulkan_headers",
-        "libnativeloader-dummy-headers",
+        "libnativeloader-headers",
         "vulkan_headers",
     ],
     export_header_lib_headers: ["vulkan_headers"],