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

Commit 51329b72 authored by Benjamin Schwartz's avatar Benjamin Schwartz
Browse files

statsd: Migrate statsd SubsystemSleepState to use power.stats HAL

statsd pulled event SubsystemSleepState will now use power.stats HAL if it
is available and supported. Otherwise it will fall back to the original
implementation of using the Power HAL.

Bug: 120551881
Test: adb shell cmd stats pull-source 10005
Test: Removed IPowerStats from manifest and ran above command. Verified
that it fell back to using IPower HAL
Test: Killed HAL services, waited for them to start back up, and
verified that statsd was still able to pull the SubsystemSleepState.
Change-Id: I852aa3386b2f2e77865ea6b4ff4cdc6e48cee77c
parent ac9a663d
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -2384,16 +2384,23 @@ message KernelWakelock {
}

/**
 * Pulls low power state information. This includes platform and subsystem sleep state information,
 * PowerStatePlatformSleepState, PowerStateVoter or PowerStateSubsystemSleepState as defined in
 * Pulls low power state information. If power.stats HAL is not available, this
 * includes platform and subsystem sleep state information,
 * PowerStatePlatformSleepState, PowerStateVoter or PowerStateSubsystemSleepState
 * as defined in:
 *   hardware/interfaces/power/1.0/types.hal
 *   hardware/interfaces/power/1.1/types.hal
 * If power.stats HAL is available, this includes PowerEntityStateResidencyResult
 * as defined in:
 *   hardware/interfaces/power/stats/1.0/types.hal
 */
message SubsystemSleepState {
    // Subsystem name
    optional string subsystem_name = 1;
    // For PlatformLowPowerStats (hal 1.0), this is the voter name, which could be empty.
    // For SubsystemLowPowerStats (hal 1.1), this is the sleep state name.
    // For PowerEntityStateResidencyResult (hal power/stats/1.0) this is the
    //    powerEntityStateName from the corresponding PowerEntityStateInfo.
    optional string subname = 2;
    // The number of times it entered, or voted for entering the sleep state
    optional uint64 count = 3;
+251 −50
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@

#include <android/hardware/power/1.0/IPower.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/stats/1.0/IPowerStats.h>

#include <fcntl.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
@@ -42,9 +44,12 @@ using android::hardware::hidl_vec;
using android::hardware::power::V1_0::IPower;
using android::hardware::power::V1_0::PowerStatePlatformSleepState;
using android::hardware::power::V1_0::PowerStateVoter;
using android::hardware::power::V1_0::Status;
using android::hardware::power::V1_1::PowerStateSubsystem;
using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
using android::hardware::power::stats::V1_0::PowerEntityInfo;
using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
using android::hardware::power::stats::V1_0::PowerEntityStateSpace;

using android::hardware::Return;
using android::hardware::Void;

@@ -55,44 +60,209 @@ namespace android {
namespace os {
namespace statsd {

std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};

sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;

std::unordered_map<uint32_t, std::string> gEntityNames = {};
std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};

std::mutex gPowerHalMutex;
bool gPowerHalExists = true;

bool getPowerHal() {
    if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
        if (gPowerHalV1_0 != nullptr) {
            gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
            ALOGI("Loaded power HAL service");
        } else {
            ALOGW("Couldn't load power HAL service");
            gPowerHalExists = false;
// The caller must be holding gPowerHalMutex.
void deinitPowerStatsLocked() {
    gPowerHalV1_0 = nullptr;
    gPowerHalV1_1 = nullptr;
    gPowerStatsHalV1_0 = nullptr;
}

struct PowerHalDeathRecipient : virtual public hardware::hidl_death_recipient {
    virtual void serviceDied(uint64_t cookie,
        const wp<android::hidl::base::V1_0::IBase>& who) override {
        // The HAL just died. Reset all handles to HAL services.
        std::lock_guard<std::mutex> lock(gPowerHalMutex);
        deinitPowerStatsLocked();
    }
    return gPowerHalV1_0 != nullptr;
};

sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();

SubsystemSleepStatePuller::SubsystemSleepStatePuller() :
    StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
}

SubsystemSleepStatePuller::SubsystemSleepStatePuller() : StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
// The caller must be holding gPowerHalMutex.
bool checkResultLocked(const Return<void> &ret, const char* function) {
    if (!ret.isOk()) {
        ALOGE("%s failed: requested HAL service not available. Description: %s",
            function, ret.description().c_str());
        if (ret.isDeadObject()) {
            deinitPowerStatsLocked();
        }
        return false;
    }
    return true;
}

bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
    std::lock_guard<std::mutex> lock(gPowerHalMutex);
// The caller must be holding gPowerHalMutex.
// gPowerStatsHalV1_0 must not be null
bool initializePowerStats() {
    using android::hardware::power::stats::V1_0::Status;

    // Clear out previous content if we are re-initializing
    gEntityNames.clear();
    gStateNames.clear();

    Return<void> ret;
    ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
        if (status != Status::SUCCESS) {
            ALOGE("Error getting power entity info");
            return;
        }

        // construct lookup table of powerEntityId to power entity name
        for (auto info : infos) {
            gEntityNames.emplace(info.powerEntityId, info.powerEntityName);
        }
    });
    if (!checkResultLocked(ret, __func__)) {
        return false;
    }

    ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
        if (status != Status::SUCCESS) {
            ALOGE("Error getting state info");
            return;
        }

        // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
        for (auto stateSpace : stateSpaces) {
            std::unordered_map<uint32_t, std::string> stateNames = {};
            for (auto state : stateSpace.states) {
                stateNames.emplace(state.powerEntityStateId,
                    state.powerEntityStateName);
            }
            gStateNames.emplace(stateSpace.powerEntityId, stateNames);
        }
    });
    if (!checkResultLocked(ret, __func__)) {
        return false;
    }

    return (!gEntityNames.empty()) && (!gStateNames.empty());
}

// The caller must be holding gPowerHalMutex.
bool getPowerStatsHalLocked() {
    if(gPowerStatsHalV1_0 == nullptr) {
        gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
        if (gPowerStatsHalV1_0 == nullptr) {
            ALOGE("Unable to get power.stats HAL service.");
            return false;
        }

        // Link death recipient to power.stats service handle
        hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
        if (!linked.isOk()) {
            ALOGE("Transaction error in linking to power.stats HAL death: %s",
                    linked.description().c_str());
            deinitPowerStatsLocked();
            return false;
        } else if (!linked) {
            ALOGW("Unable to link to power.stats HAL death notifications");
            // We should still continue even though linking failed
        }
        return initializePowerStats();
    }
    return true;
}

    if (!getPowerHal()) {
        ALOGE("Power Hal not loaded");
// The caller must be holding gPowerHalMutex.
bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
    using android::hardware::power::stats::V1_0::Status;

    if(!getPowerStatsHalLocked()) {
        return false;
    }

    int64_t wallClockTimestampNs = getWallClockNs();
    int64_t elapsedTimestampNs = getElapsedRealtimeNs();

    data->clear();
    // Get power entity state residency data
    bool success = false;
    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
        [&data, &success, wallClockTimestampNs, elapsedTimestampNs]
        (auto results, auto status) {
        if (status == Status::NOT_SUPPORTED) {
            ALOGW("getPowerEntityStateResidencyData is not supported");
            success = false;
            return;
        }

        for(auto result : results) {
            for(auto stateResidency : result.stateResidencyData) {
                auto statePtr = make_shared<LogEvent>(
                        android::util::SUBSYSTEM_SLEEP_STATE,
                        wallClockTimestampNs, elapsedTimestampNs);
                statePtr->write(gEntityNames.at(result.powerEntityId));
                statePtr->write(gStateNames.at(result.powerEntityId)
                    .at(stateResidency.powerEntityStateId));
                statePtr->write(stateResidency.totalStateEntryCount);
                statePtr->write(stateResidency.totalTimeInStateMs);
                statePtr->init();
                data->emplace_back(statePtr);
            }
        }
        success = true;
    });
    // Intentionally not returning early here.
    // bool success determines if this succeeded or not.
    checkResultLocked(ret, __func__);

    return success;
}

// The caller must be holding gPowerHalMutex.
bool getPowerHalLocked() {
    if(gPowerHalV1_0 == nullptr) {
        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
        if(gPowerHalV1_0 == nullptr) {
            ALOGE("Unable to get power HAL service.");
            return false;
        }
        gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);

        // Link death recipient to power service handle
        hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0);
        if (!linked.isOk()) {
            ALOGE("Transaction error in linking to power HAL death: %s",
                    linked.description().c_str());
            gPowerHalV1_0 = nullptr;
            return false;
        } else if (!linked) {
            ALOGW("Unable to link to power. death notifications");
            // We should still continue even though linking failed
        }
    }
    return true;
}

// The caller must be holding gPowerHalMutex.
bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
    using android::hardware::power::V1_0::Status;

    if(!getPowerHalLocked()) {
        return false;
    }

    int64_t wallClockTimestampNs = getWallClockNs();
    int64_t elapsedTimestampNs = getElapsedRealtimeNs();
        Return<void> ret;
        ret = gPowerHalV1_0->getPlatformLowPowerStats(
                [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
                [&data, wallClockTimestampNs, elapsedTimestampNs]
                    (hidl_vec<PowerStatePlatformSleepState> states, Status status) {
                    if (status != Status::SUCCESS) return;

                    for (size_t i = 0; i < states.size(); i++) {
@@ -128,9 +298,7 @@ bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data)
                        }
                    }
                });
        if (!ret.isOk()) {
            ALOGE("getLowPowerStats() failed: power HAL service not available");
            gPowerHalV1_0 = nullptr;
        if (!checkResultLocked(ret, __func__)) {
            return false;
        }

@@ -139,7 +307,8 @@ bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data)
                android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
        if (gPowerHal_1_1 != nullptr) {
            ret = gPowerHal_1_1->getSubsystemLowPowerStats(
                    [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
            [&data, wallClockTimestampNs, elapsedTimestampNs]
            (hidl_vec<PowerStateSubsystem> subsystems, Status status) {
                if (status != Status::SUCCESS) return;

                if (subsystems.size() > 0) {
@@ -170,6 +339,38 @@ bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data)
        return true;
}

// The caller must be holding gPowerHalMutex.
std::function<bool(vector<shared_ptr<LogEvent>>* data)> getPullerLocked() {
    std::function<bool(vector<shared_ptr<LogEvent>>* data)> ret = {};

    // First see if power.stats HAL is available. Fall back to power HAL if
    // power.stats HAL is unavailable.
    if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
        ALOGI("Using power.stats HAL");
        ret = getIPowerStatsDataLocked;
    } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) {
        ALOGI("Using power HAL");
        ret = getIPowerDataLocked;
    }

    return ret;
}

bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
    std::lock_guard<std::mutex> lock(gPowerHalMutex);

    if(!gPuller) {
        gPuller = getPullerLocked();
    }

    if(gPuller) {
        return gPuller(data);
    }

    ALOGE("Unable to load Power Hal or power.stats HAL");
    return false;
}

}  // namespace statsd
}  // namespace os
}  // namespace android