Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88b2a11a authored by Benjamin Schwartz's avatar Benjamin Schwartz Committed by android-build-merger
Browse files

Merge "power.stats: Add more informative default implementation"

am: bc93126d

Change-Id: I545c27e5cc196f95a5676a650cb920ed4d0aef47
parents ee43c27f bc93126d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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"],
+3 −0
Original line number Diff line number Diff line
krossmo@google.com
bsschwar@google.com
tstrudel@google.com
+201 −8
Original line number Diff line number Diff line
@@ -287,25 +287,218 @@ Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate
    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;
    // 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();
}

+19 −0
Original line number Diff line number Diff line
@@ -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 @@ struct OnDeviceMmt {
    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 @@ struct PowerStats : public IPowerStats {
        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
+53 −1
Original line number Diff line number Diff line
@@ -31,17 +31,69 @@ using android::hardware::joinRpcThreadpool;

// 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();