power.stats: Add more informative default implementation

The original default implementation for power.stats HAL did not have a
good example implementation of the power statistics related APIs. Adding
a new default implementation that gives a more informative example

Bug: 122267057
Test: run vts -m VtsHalPowerStatsV1_0Target
Test: adb shell "lshal debug android.hardware.power.stats@1.0::IPowerStats/default"
Observed the following output
========== PowerStats HAL 1.0 state residencies ==========
          Entity            State         Total time     Total entries   Last entry timestamp
   DefaultEntity           Active               1 ms                 2               3 ms
   DefaultEntity            Sleep               4 ms                 5               6 ms
========== End of PowerStats HAL 1.0 state residencies ==========

Change-Id: Ida0951c267f609b16bb6406da150ed2e504ced9a
diff --git a/power/stats/1.0/default/Android.bp b/power/stats/1.0/default/Android.bp
index 04270c1..b57466d 100644
--- a/power/stats/1.0/default/Android.bp
+++ b/power/stats/1.0/default/Android.bp
@@ -11,7 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 
-cc_library_shared {
+cc_binary {
     name: "android.hardware.power.stats@1.0-service",
     relative_install_path: "hw",
     init_rc: ["android.hardware.power.stats@1.0-service.rc"],
diff --git a/power/stats/1.0/default/OWNERS b/power/stats/1.0/default/OWNERS
new file mode 100644
index 0000000..2d95a97
--- /dev/null
+++ b/power/stats/1.0/default/OWNERS
@@ -0,0 +1,3 @@
+krossmo@google.com
+bsschwar@google.com
+tstrudel@google.com
diff --git a/power/stats/1.0/default/PowerStats.cpp b/power/stats/1.0/default/PowerStats.cpp
index 810c575..350aa62 100644
--- a/power/stats/1.0/default/PowerStats.cpp
+++ b/power/stats/1.0/default/PowerStats.cpp
@@ -287,25 +287,218 @@
     return Void();
 }
 
+uint32_t PowerStats::addPowerEntity(const std::string& name, PowerEntityType type) {
+    uint32_t id = mPowerEntityInfos.size();
+    mPowerEntityInfos.push_back({id, name, type});
+    return id;
+}
+
+void PowerStats::addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p) {
+    std::vector<PowerEntityStateSpace> stateSpaces = p->getStateSpaces();
+    for (auto stateSpace : stateSpaces) {
+        mPowerEntityStateSpaces.emplace(stateSpace.powerEntityId, stateSpace);
+        mStateResidencyDataProviders.emplace(stateSpace.powerEntityId, p);
+    }
+}
+
 Return<void> PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) {
-    hidl_vec<PowerEntityInfo> eInfo;
-    _hidl_cb(eInfo, Status::NOT_SUPPORTED);
+    // If not configured, return NOT_SUPPORTED
+    if (mPowerEntityInfos.empty()) {
+        _hidl_cb({}, Status::NOT_SUPPORTED);
+        return Void();
+    }
+
+    _hidl_cb(mPowerEntityInfos, Status::SUCCESS);
     return Void();
 }
 
 Return<void> PowerStats::getPowerEntityStateInfo(const hidl_vec<uint32_t>& powerEntityIds,
                                                  getPowerEntityStateInfo_cb _hidl_cb) {
-    (void)powerEntityIds;
-    hidl_vec<PowerEntityStateSpace> powerEntityStateSpaces;
-    _hidl_cb(powerEntityStateSpaces, Status::NOT_SUPPORTED);
+    // If not configured, return NOT_SUPPORTED
+    if (mPowerEntityStateSpaces.empty()) {
+        _hidl_cb({}, Status::NOT_SUPPORTED);
+        return Void();
+    }
+
+    std::vector<PowerEntityStateSpace> stateSpaces;
+
+    // If powerEntityIds is empty then return state space info for all entities
+    if (powerEntityIds.size() == 0) {
+        stateSpaces.reserve(mPowerEntityStateSpaces.size());
+        for (auto i : mPowerEntityStateSpaces) {
+            stateSpaces.emplace_back(i.second);
+        }
+        _hidl_cb(stateSpaces, Status::SUCCESS);
+        return Void();
+    }
+
+    // Return state space information only for valid ids
+    auto ret = Status::SUCCESS;
+    stateSpaces.reserve(powerEntityIds.size());
+    for (const uint32_t id : powerEntityIds) {
+        auto stateSpace = mPowerEntityStateSpaces.find(id);
+        if (stateSpace != mPowerEntityStateSpaces.end()) {
+            stateSpaces.emplace_back(stateSpace->second);
+        } else {
+            ret = Status::INVALID_INPUT;
+        }
+    }
+
+    _hidl_cb(stateSpaces, ret);
     return Void();
 }
 
 Return<void> PowerStats::getPowerEntityStateResidencyData(
-    const hidl_vec<uint32_t>& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
-    (void)powerEntityIds;
+        const hidl_vec<uint32_t>& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
+    // If not configured, return NOT_SUPPORTED
+    if (mStateResidencyDataProviders.empty() || mPowerEntityStateSpaces.empty()) {
+        _hidl_cb({}, Status::NOT_SUPPORTED);
+        return Void();
+    }
+
+    // If powerEntityIds is empty then return data for all supported entities
+    if (powerEntityIds.size() == 0) {
+        std::vector<uint32_t> ids;
+        for (auto stateSpace : mPowerEntityStateSpaces) {
+            ids.emplace_back(stateSpace.first);
+        }
+        return getPowerEntityStateResidencyData(ids, _hidl_cb);
+    }
+
+    std::unordered_map<uint32_t, PowerEntityStateResidencyResult> stateResidencies;
+    std::vector<PowerEntityStateResidencyResult> results;
+    results.reserve(powerEntityIds.size());
+
+    // return results for only the given powerEntityIds
+    bool invalidInput = false;
+    bool filesystemError = false;
+    for (auto id : powerEntityIds) {
+        auto dataProvider = mStateResidencyDataProviders.find(id);
+        // skip if the given powerEntityId does not have an associated StateResidencyDataProvider
+        if (dataProvider == mStateResidencyDataProviders.end()) {
+            invalidInput = true;
+            continue;
+        }
+
+        // get the results if we have not already done so.
+        if (stateResidencies.find(id) == stateResidencies.end()) {
+            if (!dataProvider->second->getResults(stateResidencies)) {
+                filesystemError = true;
+            }
+        }
+
+        // append results
+        auto stateResidency = stateResidencies.find(id);
+        if (stateResidency != stateResidencies.end()) {
+            results.emplace_back(stateResidency->second);
+        }
+    }
+
+    auto ret = Status::SUCCESS;
+    if (filesystemError) {
+        ret = Status::FILESYSTEM_ERROR;
+    } else if (invalidInput) {
+        ret = Status::INVALID_INPUT;
+    }
+
+    _hidl_cb(results, ret);
+    return Void();
+}
+
+bool DumpResidencyDataToFd(const hidl_vec<PowerEntityInfo>& infos,
+                           const hidl_vec<PowerEntityStateSpace>& stateSpaces,
+                           const hidl_vec<PowerEntityStateResidencyResult>& results, int fd) {
+    // construct lookup table of powerEntityId to name
+    std::unordered_map<uint32_t, std::string> entityNames;
+    for (auto info : infos) {
+        entityNames.emplace(info.powerEntityId, info.powerEntityName);
+    }
+
+    // construct lookup table of powerEntityId, powerEntityStateId to state name
+    std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames;
+    for (auto stateSpace : stateSpaces) {
+        stateNames.emplace(stateSpace.powerEntityId, std::unordered_map<uint32_t, std::string>());
+        for (auto state : stateSpace.states) {
+            stateNames.at(stateSpace.powerEntityId)
+                    .emplace(state.powerEntityStateId, state.powerEntityStateName);
+        }
+    }
+
+    std::ostringstream dumpStats;
+    dumpStats << "\n========== PowerStats HAL 1.0 state residencies ==========\n";
+
+    const char* headerFormat = "  %14s   %14s   %16s   %15s   %16s\n";
+    const char* dataFormat =
+            "  %14s   %14s   %13" PRIu64 " ms   %15" PRIu64 "   %13" PRIu64 " ms\n";
+    dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
+                                             "Total entries", "Last entry timestamp");
+
+    for (auto result : results) {
+        for (auto stateResidency : result.stateResidencyData) {
+            dumpStats << android::base::StringPrintf(
+                    dataFormat, entityNames.at(result.powerEntityId).c_str(),
+                    stateNames.at(result.powerEntityId)
+                            .at(stateResidency.powerEntityStateId)
+                            .c_str(),
+                    stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
+                    stateResidency.lastEntryTimestampMs);
+        }
+    }
+
+    dumpStats << "========== End of PowerStats HAL 1.0 state residencies ==========\n";
+
+    return android::base::WriteStringToFd(dumpStats.str(), fd);
+}
+
+Return<void> PowerStats::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
+    if (handle == nullptr || handle->numFds < 1) {
+        return Void();
+    }
+
+    int fd = handle->data[0];
+    Status status;
+    hidl_vec<PowerEntityInfo> infos;
+
+    // Get power entity information
+    getPowerEntityInfo([&status, &infos](auto rInfos, auto rStatus) {
+        status = rStatus;
+        infos = rInfos;
+    });
+    if (status != Status::SUCCESS) {
+        LOG(ERROR) << "Error getting power entity info";
+        return Void();
+    }
+
+    // Get power entity state information
+    hidl_vec<PowerEntityStateSpace> stateSpaces;
+    getPowerEntityStateInfo({}, [&status, &stateSpaces](auto rStateSpaces, auto rStatus) {
+        status = rStatus;
+        stateSpaces = rStateSpaces;
+    });
+    if (status != Status::SUCCESS) {
+        LOG(ERROR) << "Error getting state info";
+        return Void();
+    }
+
+    // Get power entity state residency data
     hidl_vec<PowerEntityStateResidencyResult> results;
-    _hidl_cb(results, Status::NOT_SUPPORTED);
+    getPowerEntityStateResidencyData({}, [&status, &results](auto rResults, auto rStatus) {
+        status = rStatus;
+        results = rResults;
+    });
+
+    // This implementation of getPowerEntityStateResidencyData supports the
+    // return of partial results if status == FILESYSTEM_ERROR.
+    if (status != Status::SUCCESS) {
+        LOG(ERROR) << "Error getting residency data -- Some results missing";
+    }
+
+    if (!DumpResidencyDataToFd(infos, stateSpaces, results, fd)) {
+        PLOG(ERROR) << "Failed to dump residency data to fd";
+    }
+
+    fsync(fd);
+
     return Void();
 }
 
diff --git a/power/stats/1.0/default/PowerStats.h b/power/stats/1.0/default/PowerStats.h
index fb2c6a8..d67fb35 100644
--- a/power/stats/1.0/default/PowerStats.h
+++ b/power/stats/1.0/default/PowerStats.h
@@ -21,6 +21,7 @@
 #include <fmq/MessageQueue.h>
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
+#include <unordered_map>
 
 namespace android {
 namespace hardware {
@@ -60,8 +61,19 @@
     std::unique_ptr<MessageQueueSync> fmqSynchronized;
 };
 
+class IStateResidencyDataProvider {
+   public:
+    virtual ~IStateResidencyDataProvider() = default;
+    virtual bool getResults(
+            std::unordered_map<uint32_t, PowerEntityStateResidencyResult>& results) = 0;
+    virtual std::vector<PowerEntityStateSpace> getStateSpaces() = 0;
+};
+
 struct PowerStats : public IPowerStats {
+   public:
     PowerStats();
+    uint32_t addPowerEntity(const std::string& name, PowerEntityType type);
+    void addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p);
     // Methods from ::android::hardware::power::stats::V1_0::IPowerStats follow.
     Return<void> getRailInfo(getRailInfo_cb _hidl_cb) override;
     Return<void> getEnergyData(const hidl_vec<uint32_t>& railIndices,
@@ -75,12 +87,19 @@
         const hidl_vec<uint32_t>& powerEntityIds,
         getPowerEntityStateResidencyData_cb _hidl_cb) override;
 
+    // Methods from ::android::hidl::base::V1_0::IBase follow.
+    Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+
    private:
     OnDeviceMmt mPm;
     void findIioPowerMonitorNodes();
     size_t parsePowerRails();
     int parseIioEnergyNode(std::string devName);
     Status parseIioEnergyNodes();
+    std::vector<PowerEntityInfo> mPowerEntityInfos;
+    std::unordered_map<uint32_t, PowerEntityStateSpace> mPowerEntityStateSpaces;
+    std::unordered_map<uint32_t, std::shared_ptr<IStateResidencyDataProvider>>
+            mStateResidencyDataProviders;
 };
 
 }  // namespace implementation
diff --git a/power/stats/1.0/default/service.cpp b/power/stats/1.0/default/service.cpp
index 80649f5..a516536 100644
--- a/power/stats/1.0/default/service.cpp
+++ b/power/stats/1.0/default/service.cpp
@@ -31,17 +31,69 @@
 
 // Generated HIDL files
 using android::hardware::power::stats::V1_0::IPowerStats;
+using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
+using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
+using android::hardware::power::stats::V1_0::PowerEntityType;
+using android::hardware::power::stats::V1_0::implementation::IStateResidencyDataProvider;
 using android::hardware::power::stats::V1_0::implementation::PowerStats;
 
+class DefaultStateResidencyDataProvider : public IStateResidencyDataProvider {
+   public:
+    DefaultStateResidencyDataProvider(uint32_t id)
+        : mPowerEntityId(id), mActiveStateId(0), mSleepStateId(1) {}
+    ~DefaultStateResidencyDataProvider() = default;
+
+    bool getResults(std::unordered_map<uint32_t, PowerEntityStateResidencyResult>& results) {
+        PowerEntityStateResidencyResult result = { .powerEntityId = mPowerEntityId };
+        result.stateResidencyData.resize(2);
+
+        // Using fake numbers here for display only. A real implementation would
+        // use actual tracked stats.
+        result.stateResidencyData[0] = {
+            .powerEntityStateId = mActiveStateId,
+            .totalTimeInStateMs = 1,
+            .totalStateEntryCount = 2,
+            .lastEntryTimestampMs = 3
+        };
+        result.stateResidencyData[1] = {
+            .powerEntityStateId = mSleepStateId,
+            .totalTimeInStateMs = 4,
+            .totalStateEntryCount = 5,
+            .lastEntryTimestampMs = 6,
+        };
+        results.emplace(mPowerEntityId, result);
+        return true;
+    }
+
+    std::vector<PowerEntityStateSpace> getStateSpaces() {
+        return {{
+          .powerEntityId = mPowerEntityId,
+          .states = {
+              {.powerEntityStateId = mActiveStateId, .powerEntityStateName = "Active"},
+              {.powerEntityStateId = mSleepStateId, .powerEntityStateName = "Sleep"}
+          }
+        }};
+    }
+
+   private:
+    const uint32_t mPowerEntityId;
+    const uint32_t mActiveStateId;
+    const uint32_t mSleepStateId;
+};
+
 int main(int /* argc */, char** /* argv */) {
     ALOGI("power.stats service 1.0 is starting.");
 
-    android::sp<IPowerStats> service = new PowerStats();
+    PowerStats* service = new PowerStats();
     if (service == nullptr) {
         ALOGE("Can not create an instance of power.stats HAL Iface, exiting.");
         return 1;
     }
 
+    uint32_t defaultId = service->addPowerEntity("DefaultEntity", PowerEntityType::SUBSYSTEM);
+    auto defaultSdp = std::make_shared<DefaultStateResidencyDataProvider>(defaultId);
+    service->addStateResidencyDataProvider(std::move(defaultSdp));
+
     configureRpcThreadpool(1, true /*callerWillJoin*/);
 
     status_t status = service->registerAsService();