diff --git a/device.mk b/device.mk
index d9ee5e0..d6a156c 100644
--- a/device.mk
+++ b/device.mk
@@ -16,3 +16,6 @@
 
 PRODUCT_COPY_FILES += \
 	device/google/gs-common/default-talkback-permissions.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/default-permissions/default-talkback-permissions.xml
+
+PRODUCT_SOONG_NAMESPACES += \
+	device/google/gs-common/powerstats
diff --git a/powerstats/Android.bp b/powerstats/Android.bp
new file mode 100644
index 0000000..74935c7
--- /dev/null
+++ b/powerstats/Android.bp
@@ -0,0 +1,24 @@
+soong_namespace {
+    imports: [
+        "hardware/google/pixel",
+    ],
+}
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+    name: "android.hardware.power.stats-impl.gs-common",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    defaults: ["powerstats_pixel_defaults"],
+
+    srcs: [
+        "*.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.power.stats-impl.pixel",
+    ],
+}
diff --git a/powerstats/AocStateResidencyDataProvider.cpp b/powerstats/AocStateResidencyDataProvider.cpp
new file mode 100644
index 0000000..c64496d
--- /dev/null
+++ b/powerstats/AocStateResidencyDataProvider.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 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 "AocStateResidencyDataProvider.h"
+
+#include <android-base/logging.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+AocStateResidencyDataProvider::AocStateResidencyDataProvider(std::vector<std::pair<std::string,
+        std::string>> ids, std::vector<std::pair<std::string, std::string>> states) {
+    // AoC stats are reported in ticks of 244.140625ns. The transform
+    // function converts ticks to milliseconds.
+    // 1000000 / 244.140625 = 4096.
+    static const uint64_t AOC_CLK = 4096;
+    std::function<uint64_t(uint64_t)> aocTickToMs = [](uint64_t a) { return a / AOC_CLK; };
+    GenericStateResidencyDataProvider::StateResidencyConfig config = {
+            .entryCountSupported = true,
+            .entryCountPrefix = "Counter:",
+            .totalTimeSupported = true,
+            .totalTimePrefix = "Cumulative time:",
+            .totalTimeTransform = aocTickToMs,
+            .lastEntrySupported = true,
+            .lastEntryPrefix = "Time last entered:",
+            .lastEntryTransform = aocTickToMs,
+    };
+    for (const auto &id : ids) {
+        for (const auto &state : states) {
+            std::vector<std::pair<std::string, std::string>> aocStateHeaders = {
+                std::make_pair(state.first, ""),
+            };
+            std::vector<GenericStateResidencyDataProvider::PowerEntityConfig> cfgs;
+            cfgs.emplace_back(generateGenericStateResidencyConfigs(config, aocStateHeaders),
+                    id.first, "");
+            std::unique_ptr<GenericStateResidencyDataProvider> sdp(
+                    new GenericStateResidencyDataProvider(id.second + state.second, cfgs));
+            mProviders[id.first].push_back(std::move(sdp));
+        }
+    }
+}
+
+bool AocStateResidencyDataProvider::getStateResidencies(
+        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
+    // States from the same power entity are merged.
+    bool ret = true;
+    for (const auto &providerList : mProviders) {
+        int32_t stateId = 0;
+        std::string curEntity = providerList.first;
+        std::vector<StateResidency> stateResidencies;
+
+        // Iterate over each provider in the providerList, appending each of the states
+        for (const auto &provider : providerList.second) {
+            std::unordered_map<std::string, std::vector<StateResidency>> residency;
+            ret &= provider->getStateResidencies(&residency);
+
+            // Each provider should only return data for curEntity but checking anyway
+            if (residency.find(curEntity) != residency.end()) {
+                for (auto &r : residency.at(curEntity)) {
+                    /*
+                     * Modifying stateId here because we are stitching together infos from
+                     * multiple GenericStateResidencyDataProviders. stateId must be modified
+                     * to maintain uniqueness for a given entity
+                     */
+                    r.id = stateId++;
+                    stateResidencies.push_back(r);
+                }
+            }
+        }
+
+        residencies->emplace(curEntity, stateResidencies);
+    }
+    return ret;
+}
+
+std::unordered_map<std::string, std::vector<State>> AocStateResidencyDataProvider::getInfo() {
+    // States from the same power entity are merged
+    std::unordered_map<std::string, std::vector<State>> infos;
+    for (const auto &providerList : mProviders) {
+        int32_t stateId = 0;
+        std::string curEntity = providerList.first;
+        std::vector<State> stateInfos;
+
+        // Iterate over each provider in the providerList, appending each of the states
+        for (const auto &provider : providerList.second) {
+            std::unordered_map<std::string, std::vector<State>> info = provider->getInfo();
+
+            // Each provider should only return data for curEntity but checking anyway
+            if (info.find(curEntity) != info.end()) {
+                for (auto &i : info.at(curEntity)) {
+                    /*
+                     * Modifying stateId because we are stitching together infos from
+                     * multiple GenericStateResidencyDataProviders. stateId must be modified
+                     * to maintain uniqueness for a given entity
+                     */
+                    i.id = stateId++;
+                    stateInfos.push_back(i);
+                }
+            }
+        }
+
+        infos.emplace(curEntity, stateInfos);
+    }
+
+    return infos;
+}
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/powerstats/DevfreqStateResidencyDataProvider.cpp b/powerstats/DevfreqStateResidencyDataProvider.cpp
new file mode 100644
index 0000000..d59e1e5
--- /dev/null
+++ b/powerstats/DevfreqStateResidencyDataProvider.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 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 "DevfreqStateResidencyDataProvider.h"
+
+#include <android-base/logging.h>
+
+static const std::string nameSuffix = "-DVFS";
+static const std::string pathSuffix = "/time_in_state";
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+DevfreqStateResidencyDataProvider::DevfreqStateResidencyDataProvider(const std::string& name,
+        const std::string& path) : mName(name + nameSuffix), mPath(path + pathSuffix) {}
+
+bool DevfreqStateResidencyDataProvider::extractNum(const char *str, char **str_end, int base,
+        int64_t* num) {
+    // errno can be set to any non-zero value by a library function call
+    // regardless of whether there was an error, so it needs to be cleared
+    // in order to check the error set by strtoll
+    errno = 0;
+    *num = std::strtoll(str, str_end, base);
+    return (errno != ERANGE);
+}
+
+std::vector<std::pair<int64_t, int64_t>> DevfreqStateResidencyDataProvider::parseTimeInState() {
+    // Using FILE* instead of std::ifstream for performance reasons
+    std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(mPath.c_str(), "r"), fclose);
+    if (!fp) {
+        PLOG(ERROR) << "Failed to open file " << mPath;
+        return {};
+    }
+
+    std::vector<std::pair<int64_t, int64_t>> timeInState;
+
+    char *line = nullptr;
+    size_t len = 0;
+    while (getline(&line, &len, fp.get()) != -1) {
+        char* pEnd;
+        int64_t frequencyHz, totalTimeMs;
+        if (!extractNum(line, &pEnd, 10, &frequencyHz) ||
+            !extractNum(pEnd, &pEnd, 10, &totalTimeMs)) {
+            PLOG(ERROR) << "Failed to parse " << mPath;
+            free(line);
+            return {};
+        }
+
+        timeInState.push_back({frequencyHz, totalTimeMs});
+    }
+
+    free(line);
+    return timeInState;
+}
+
+bool DevfreqStateResidencyDataProvider::getStateResidencies(
+        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
+    std::vector<std::pair<int64_t, int64_t>> timeInState = parseTimeInState();
+
+    if (timeInState.empty()) {
+        return false;
+    }
+
+    int32_t id = 0;
+    std::vector<StateResidency> stateResidencies;
+    for (const auto[frequencyHz, totalTimeMs] : timeInState) {
+        StateResidency s = {.id = id++, .totalTimeInStateMs = totalTimeMs};
+        stateResidencies.push_back(s);
+    }
+
+    residencies->emplace(mName, stateResidencies);
+    return true;
+}
+
+std::unordered_map<std::string, std::vector<State>> DevfreqStateResidencyDataProvider::getInfo() {
+    std::vector<std::pair<int64_t, int64_t>> timeInState = parseTimeInState();
+
+    if (timeInState.empty()) {
+        return {};
+    }
+
+    int32_t id = 0;
+    std::vector<State> states;
+    for (const auto[frequencyHz, totalTimeMs] : timeInState) {
+        State s = {.id = id++, .name = std::to_string(frequencyHz / 1000) + "MHz"};
+        states.push_back(s);
+    }
+
+    return {{mName, states}};
+}
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/powerstats/DvfsStateResidencyDataProvider.cpp b/powerstats/DvfsStateResidencyDataProvider.cpp
new file mode 100644
index 0000000..511159e
--- /dev/null
+++ b/powerstats/DvfsStateResidencyDataProvider.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2020 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 "DvfsStateResidencyDataProvider.h"
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include <string>
+#include <utility>
+
+using android::base::ParseUint;
+using android::base::Split;
+using android::base::StartsWith;
+using android::base::Trim;
+
+static const std::string nameSuffix = "-DVFS";
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+DvfsStateResidencyDataProvider::DvfsStateResidencyDataProvider(std::string path, uint64_t clockRate,
+        std::vector<Config> cfgs)
+    : mPath(std::move(path)), mClockRate(clockRate), mPowerEntities(std::move(cfgs)) {}
+
+int32_t DvfsStateResidencyDataProvider::matchEntity(char const *line) {
+    for (int32_t i = 0; i < mPowerEntities.size(); i++) {
+        if (mPowerEntities[i].powerEntityName == Trim(std::string(line))) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+int32_t DvfsStateResidencyDataProvider::matchState(char const *line, const Config& powerEntity) {
+    for (int32_t i = 0; i < powerEntity.states.size(); i++) {
+        if (StartsWith(Trim(std::string(line)), powerEntity.states[i].second)) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+bool DvfsStateResidencyDataProvider::parseState(char const *line, uint64_t *duration,
+        uint64_t *count) {
+    std::vector<std::string> parts = Split(line, " ");
+    if (parts.size() != 7) {
+        return false;
+    }
+    if (!ParseUint(Trim(parts[3]), count)) {
+        return false;
+    }
+    if (!ParseUint(Trim(parts[6]), duration)) {
+        return false;
+    }
+    return true;
+}
+
+bool DvfsStateResidencyDataProvider::getStateResidencies(
+        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
+    std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(mPath.c_str(), "r"), fclose);
+    if (!fp) {
+        PLOG(ERROR) << __func__ << ":Failed to open file " << mPath;
+        return false;
+    }
+
+    for (const Config &powerEntity : mPowerEntities) {
+        std::vector<StateResidency> stateResidency(powerEntity.states.size());
+        for (int32_t i = 0; i < stateResidency.size(); i++) {
+            stateResidency[i].id = i;
+        }
+        residencies->emplace(powerEntity.powerEntityName + nameSuffix, stateResidency);
+    }
+
+    size_t len = 0;
+    char *line = nullptr;
+
+    int32_t temp, powerEntityIndex, stateId = -1;
+    uint64_t duration, count;
+    auto it = residencies->end();
+
+    while (getline(&line, &len, fp.get()) != -1) {
+        temp = matchEntity(line);
+        // Assign new index only when a new valid entity is encountered.
+        if (temp >= 0) {
+            powerEntityIndex = temp;
+            it = residencies->find(mPowerEntities[powerEntityIndex].powerEntityName + nameSuffix);
+        }
+
+        if (it != residencies->end()) {
+            stateId = matchState(line, mPowerEntities[powerEntityIndex]);
+
+            if (stateId >= 0) {
+                if (parseState(line, &duration, &count)) {
+                    it->second[stateId].totalTimeInStateMs =
+                            duration / mClockRate;
+                    it->second[stateId].totalStateEntryCount = count;
+                } else {
+                    LOG(ERROR) << "Failed to parse duration and count from [" << std::string(line)
+                               << "]";
+                    return false;
+                }
+            }
+        }
+    }
+
+    free(line);
+
+    return true;
+}
+
+std::unordered_map<std::string, std::vector<State>> DvfsStateResidencyDataProvider::getInfo() {
+    std::unordered_map<std::string, std::vector<State>> info;
+    for (auto const &entity : mPowerEntities) {
+        std::vector<State> stateInfo(entity.states.size());
+        int32_t stateId = 0;
+        for (auto const &state : entity.states) {
+            stateInfo[stateId] = State{
+                .id = stateId,
+                .name = state.first
+            };
+            stateId++;
+        }
+        info.emplace(entity.powerEntityName + nameSuffix, stateInfo);
+    }
+    return info;
+}
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/powerstats/UfsStateResidencyDataProvider.cpp b/powerstats/UfsStateResidencyDataProvider.cpp
new file mode 100644
index 0000000..aec7724
--- /dev/null
+++ b/powerstats/UfsStateResidencyDataProvider.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 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 "UfsStateResidencyDataProvider.h"
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include <string>
+#include <utility>
+
+using android::base::ParseInt;
+using android::base::Split;
+using android::base::StartsWith;
+using android::base::Trim;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+const int32_t HIBERNATE_STATE_ID = 0;
+const std::string UFS_NAME = "UFS";
+
+UfsStateResidencyDataProvider::UfsStateResidencyDataProvider(std::string prefix) : kPrefix(prefix) {}
+
+bool UfsStateResidencyDataProvider::getStateResidencies(
+        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
+    StateResidency residency;
+    residency.id = HIBERNATE_STATE_ID;
+
+        // The transform function converts microseconds to milliseconds.
+    std::function<uint64_t(uint64_t)> usecToMs = [](uint64_t a) { return a / 1000; };
+
+    residency.totalTimeInStateMs = usecToMs(readStat(kPrefix + "hibern8_total_us"));
+    residency.totalStateEntryCount = readStat(kPrefix + "hibern8_exit_cnt");
+    residency.lastEntryTimestampMs = usecToMs(readStat(kPrefix + "last_hibern8_enter_time"));
+
+    residencies->emplace(UFS_NAME, std::vector<StateResidency>{residency});
+    return true;
+}
+
+std::unordered_map<std::string, std::vector<State>> UfsStateResidencyDataProvider::getInfo() {
+    return {{UFS_NAME, std::vector<State>{{HIBERNATE_STATE_ID, "HIBERN8"}} }};
+}
+
+int64_t UfsStateResidencyDataProvider::readStat(std::string path) {
+    std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), fclose);
+    if (!fp) {
+        PLOG(ERROR) << __func__ << ":Failed to open file " << path
+                    << " Error = " << strerror(errno);
+        return 0;
+    }
+    const size_t size = 20;
+    char buf[size];
+    (void)fread(&buf, sizeof(char), size, fp.get());
+    int64_t ret;
+    if (!ParseInt(Trim(std::string(buf)), &ret)) {
+        LOG(ERROR) << "Failed to parse int64 from [" << std::string(buf) << "]";
+    }
+    return ret;
+}
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/powerstats/include/AocStateResidencyDataProvider.h b/powerstats/include/AocStateResidencyDataProvider.h
new file mode 100644
index 0000000..5008912
--- /dev/null
+++ b/powerstats/include/AocStateResidencyDataProvider.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <dataproviders/GenericStateResidencyDataProvider.h>
+#include <PowerStatsAidl.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+class AocStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider {
+  public:
+    AocStateResidencyDataProvider(std::vector<std::pair<std::string, std::string>> ids,
+                                  std::vector<std::pair<std::string, std::string>> states);
+    ~AocStateResidencyDataProvider() = default;
+    bool getStateResidencies(
+        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) override;
+    std::unordered_map<std::string, std::vector<State>> getInfo() override;
+
+  private:
+    std::unordered_map<std::string /* entity name */,
+        std::vector<std::unique_ptr<GenericStateResidencyDataProvider>> /* providers */> mProviders;
+};
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
\ No newline at end of file
diff --git a/powerstats/include/DevfreqStateResidencyDataProvider.h b/powerstats/include/DevfreqStateResidencyDataProvider.h
new file mode 100644
index 0000000..8341b43
--- /dev/null
+++ b/powerstats/include/DevfreqStateResidencyDataProvider.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <PowerStatsAidl.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+class DevfreqStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider {
+  public:
+    DevfreqStateResidencyDataProvider(const std::string& name, const std::string& path);
+    ~DevfreqStateResidencyDataProvider() = default;
+
+    /*
+     * See IStateResidencyDataProvider::getStateResidencies
+     */
+    bool getStateResidencies(
+        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) override;
+
+    /*
+     * See IStateResidencyDataProvider::getInfo
+     */
+    std::unordered_map<std::string, std::vector<State>> getInfo() override;
+
+  private:
+    bool extractNum(const char *str, char **str_end, int base, int64_t* num);
+    std::vector<std::pair<int64_t, int64_t>> parseTimeInState();
+    const std::string mName;
+    const std::string mPath;
+};
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/powerstats/include/DvfsStateResidencyDataProvider.h b/powerstats/include/DvfsStateResidencyDataProvider.h
new file mode 100644
index 0000000..ca8ab22
--- /dev/null
+++ b/powerstats/include/DvfsStateResidencyDataProvider.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <PowerStatsAidl.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+class DvfsStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider {
+  public:
+    class Config {
+      public:
+        // Power entity name to parse.
+        std::string powerEntityName;
+
+        // List of state pairs (name to display, name to parse).
+        std::vector<std::pair<std::string, std::string>> states;
+    };
+    /*
+     * path - path to dvfs sysfs node.
+     * clockRate - clock rate in KHz.
+     */
+    DvfsStateResidencyDataProvider(std::string path, uint64_t clockRate, std::vector<Config> cfgs);
+    ~DvfsStateResidencyDataProvider() = default;
+
+    /*
+     * See IStateResidencyDataProvider::getStateResidencies
+     */
+    bool getStateResidencies(
+        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) override;
+
+    /*
+     * See IStateResidencyDataProvider::getInfo
+     */
+    std::unordered_map<std::string, std::vector<State>> getInfo() override;
+
+  private:
+    int32_t matchEntity(char const *line);
+    int32_t matchState(char const *line, const Config& powerEntity);
+    bool parseState(char const *line, uint64_t *duration, uint64_t *count);
+
+    const std::string mPath;
+    const uint64_t mClockRate;
+    std::vector<Config> mPowerEntities;
+};
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/powerstats/include/UfsStateResidencyDataProvider.h b/powerstats/include/UfsStateResidencyDataProvider.h
new file mode 100644
index 0000000..f4ef268
--- /dev/null
+++ b/powerstats/include/UfsStateResidencyDataProvider.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <PowerStatsAidl.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+class UfsStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider {
+  public:
+    UfsStateResidencyDataProvider(std::string prefix);
+    ~UfsStateResidencyDataProvider() = default;
+
+    /*
+     * See IStateResidencyDataProvider::getStateResidencies
+     */
+    bool getStateResidencies(
+        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) override;
+
+    /*
+     * See IStateResidencyDataProvider::getInfo
+     */
+    std::unordered_map<std::string, std::vector<State>> getInfo() override;
+
+  private:
+    int64_t readStat(std::string path);
+
+    const std::string kPrefix;
+};
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl