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

Commit 252e8d04 authored by Benjamin Schwartz's avatar Benjamin Schwartz Committed by Android (Google) Code Review
Browse files

Merge "BatteryStats: Update to use power.stats HAL"

parents 419a2638 5626d082
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ cc_defaults {
        "android.hardware.light@2.0",
        "android.hardware.power@1.0",
        "android.hardware.power@1.1",
        "android.hardware.power.stats@1.0",
        "android.hardware.tetheroffload.config@1.0",
        "android.hardware.thermal@1.0",
        "android.hardware.tv.cec@1.0",
+394 −20
Original line number Diff line number Diff line
@@ -27,9 +27,11 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <unordered_map>

#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 <android/system/suspend/1.0/ISystemSuspend.h>
#include <android/system/suspend/1.0/ISystemSuspendCallback.h>
#include <android_runtime/AndroidRuntime.h>
@@ -74,6 +76,33 @@ static jmethodID jgetSubsystem = NULL;
static jmethodID jputVoter = NULL;
static jmethodID jputState = NULL;

std::mutex gPowerHalMutex;
std::unordered_map<uint32_t, std::string> gPowerStatsHalEntityNames = {};
std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>>
    gPowerStatsHalStateNames = {};
std::vector<uint32_t> gPowerStatsHalPlatformIds = {};
std::vector<uint32_t> gPowerStatsHalSubsystemIds = {};
sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
std::function<void(JNIEnv*, jobject)> gGetLowPowerStatsImpl = {};
std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {};
std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {};

// The caller must be holding gPowerHalMutex.
static void deinitPowerStatsLocked() {
    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();
    }
};

sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();

class WakeupCallback : public ISystemSuspendCallback {
public:
    Return<void> notifyWakeup(bool success) override {
@@ -202,18 +231,291 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
    return mergedreasonpos - mergedreason;
}

static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats) {
    if (jrpmStats == NULL) {
        jniThrowException(env, "java/lang/NullPointerException",
                "The rpmstats jni input jobject jrpmStats is null.");
// The caller must be holding gPowerHalMutex.
static 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;
}

// The caller must be holding gPowerHalMutex.
// gPowerStatsHalV1_0 must not be null
static bool initializePowerStats() {
    using android::hardware::power::stats::V1_0::Status;
    using android::hardware::power::stats::V1_0::PowerEntityType;

    // Clear out previous content if we are re-initializing
    gPowerStatsHalEntityNames.clear();
    gPowerStatsHalStateNames.clear();
    gPowerStatsHalPlatformIds.clear();
    gPowerStatsHalSubsystemIds.clear();

    Return<void> ret;
    ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
        if (status != Status::SUCCESS) {
            ALOGE("Error getting power entity info");
            return;
        }
    if (jgetAndUpdatePlatformState == NULL || jgetSubsystem == NULL
            || jputVoter == NULL || jputState == NULL) {
        ALOGE("A rpmstats jni jmethodID is null.");

        // construct lookup table of powerEntityId to power entity name
        // also construct vector of platform and subsystem IDs
        for (auto info : infos) {
            gPowerStatsHalEntityNames.emplace(info.powerEntityId, info.powerEntityName);
            if (info.type == PowerEntityType::POWER_DOMAIN) {
                gPowerStatsHalPlatformIds.emplace_back(info.powerEntityId);
            } else {
                gPowerStatsHalSubsystemIds.emplace_back(info.powerEntityId);
            }
        }
    });
    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);
            }
            gPowerStatsHalStateNames.emplace(stateSpace.powerEntityId, stateNames);
        }
    });
    if (!checkResultLocked(ret, __func__)) {
        return false;
    }

    return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty());
}

// The caller must be holding gPowerHalMutex.
static 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;
}

// The caller must be holding powerHalMutex.
static void getPowerStatsHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
    using android::hardware::power::stats::V1_0::Status;

    if (!getPowerStatsHalLocked()) {
        ALOGE("failed to get low power stats");
        return;
    }

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

            for (auto result : results) {
                jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
                    env->NewStringUTF(gPowerStatsHalEntityNames.at(result.powerEntityId).c_str()));
                if (jsubsystem == NULL) {
                    ALOGE("The rpmstats jni jobject jsubsystem is null.");
                    return;
                }
                for (auto stateResidency : result.stateResidencyData) {

                    env->CallVoidMethod(jsubsystem, jputState,
                        env->NewStringUTF(gPowerStatsHalStateNames.at(result.powerEntityId)
                        .at(stateResidency.powerEntityStateId).c_str()),
                        stateResidency.totalTimeInStateMs,
                        stateResidency.totalStateEntryCount);
                }
            }
            success = true;
        });
    checkResultLocked(ret, __func__);
    if (!success) {
        ALOGE("getPowerEntityStateResidencyData failed");
    }
}

static jint getPowerStatsHalPlatformData(JNIEnv* env, jobject outBuf) {
    using android::hardware::power::stats::V1_0::Status;
    using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
    using hardware::power::stats::V1_0::PowerEntityStateResidencyData;

    if (!getPowerStatsHalLocked()) {
        ALOGE("failed to get low power stats");
        return -1;
    }

    char *output = (char*)env->GetDirectBufferAddress(outBuf);
    char *offset = output;
    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
    int total_added = -1;

    // Get power entity state residency data
    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
        gPowerStatsHalPlatformIds,
        [&offset, &remaining, &total_added](auto results, auto status) {
            if (status == Status::NOT_SUPPORTED) {
                ALOGW("getPowerEntityStateResidencyData is not supported");
                return;
            }

            for (size_t i = 0; i < results.size(); i++) {
                const PowerEntityStateResidencyResult& result = results[i];

                for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
                    const PowerEntityStateResidencyData& stateResidency =
                        result.stateResidencyData[j];
                    int added = snprintf(offset, remaining,
                        "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
                        j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
                           .at(stateResidency.powerEntityStateId).c_str(),
                        stateResidency.totalTimeInStateMs,
                        stateResidency.totalStateEntryCount);
                    if (added < 0) {
                        break;
                    }
                    if (added > remaining) {
                        added = remaining;
                    }
                    offset += added;
                    remaining -= added;
                    total_added += added;
                }
                if (remaining <= 0) {
                    /* rewrite NULL character*/
                    offset--;
                    total_added--;
                    ALOGE("power.stats Hal: buffer not enough");
                    break;
                }
            }
        });
    if (!checkResultLocked(ret, __func__)) {
        return -1;
    }

    total_added += 1;
    return total_added;
}

static jint getPowerStatsHalSubsystemData(JNIEnv* env, jobject outBuf) {
    using android::hardware::power::stats::V1_0::Status;
    using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
    using hardware::power::stats::V1_0::PowerEntityStateResidencyData;

    if (!getPowerStatsHalLocked()) {
        ALOGE("failed to get low power stats");
        return -1;
    }

    char *output = (char*)env->GetDirectBufferAddress(outBuf);
    char *offset = output;
    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
    int total_added = -1;

    // Get power entity state residency data
    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
        gPowerStatsHalSubsystemIds,
        [&offset, &remaining, &total_added](auto results, auto status) {
            if (status == Status::NOT_SUPPORTED) {
                ALOGW("getPowerEntityStateResidencyData is not supported");
                return;
            }

            int added = snprintf(offset, remaining, "SubsystemPowerState ");
            offset += added;
            remaining -= added;
            total_added += added;

            for (size_t i = 0; i < results.size(); i++) {
                const PowerEntityStateResidencyResult& result = results[i];
                added = snprintf(offset, remaining, "subsystem_%zu name=%s ",
                        i + 1, gPowerStatsHalEntityNames.at(result.powerEntityId).c_str());
                if (added < 0) {
                    break;
                }

                if (added > remaining) {
                    added = remaining;
                }

                offset += added;
                remaining -= added;
                total_added += added;

                for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
                    const PowerEntityStateResidencyData& stateResidency =
                        result.stateResidencyData[j];
                    added = snprintf(offset, remaining,
                        "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%"
                        PRIu64 " ", j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
                           .at(stateResidency.powerEntityStateId).c_str(),
                        stateResidency.totalTimeInStateMs,
                        stateResidency.totalStateEntryCount,
                        stateResidency.lastEntryTimestampMs);
                    if (added < 0) {
                        break;
                    }
                    if (added > remaining) {
                        added = remaining;
                    }
                    offset += added;
                    remaining -= added;
                    total_added += added;
                }
                if (remaining <= 0) {
                    /* rewrite NULL character*/
                    offset--;
                    total_added--;
                    ALOGE("power.stats Hal: buffer not enough");
                    break;
                }
            }
        });
    if (!checkResultLocked(ret, __func__)) {
        return -1;
    }

    total_added += 1;
    return total_added;
}

// The caller must be holding powerHalMutex.
static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
    sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
    if (powerHalV1_0 == nullptr) {
        ALOGE("Power Hal not loaded");
@@ -286,17 +588,12 @@ static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats
    processPowerHalReturn(ret, "getSubsystemLowPowerStats");
}

static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
static jint getPowerHalPlatformData(JNIEnv* env, jobject outBuf) {
    char *output = (char*)env->GetDirectBufferAddress(outBuf);
    char *offset = output;
    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
    int total_added = -1;

    if (outBuf == NULL) {
        jniThrowException(env, "java/lang/NullPointerException", "null argument");
        return -1;
    }

    {
        sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
        if (powerHalV1_0 == nullptr) {
@@ -365,7 +662,7 @@ static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject o
    return total_added;
}

static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
static jint getPowerHalSubsystemData(JNIEnv* env, jobject outBuf) {
    char *output = (char*)env->GetDirectBufferAddress(outBuf);
    char *offset = output;
    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
@@ -374,11 +671,6 @@ static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject
    // This is a IPower 1.1 API
    sp<IPowerV1_1> powerHal_1_1 = nullptr;

    if (outBuf == NULL) {
        jniThrowException(env, "java/lang/NullPointerException", "null argument");
        return -1;
    }

    {
        // Trying to get 1.1, this will succeed only for devices supporting 1.1
        powerHal_1_1 = getPowerHalV1_1();
@@ -458,6 +750,88 @@ static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject
    return total_added;
}

static void setUpPowerStatsLocked() {
    // 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");
        gGetLowPowerStatsImpl = getPowerStatsHalLowPowerData;
        gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformData;
        gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemData;
    } else if (android::hardware::power::V1_0::IPower::getService() != nullptr) {
        ALOGI("Using power HAL");
        gGetLowPowerStatsImpl = getPowerHalLowPowerData;
        gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData;
        gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData;
    }
}

static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats) {
    if (jrpmStats == NULL) {
        jniThrowException(env, "java/lang/NullPointerException",
                "The rpmstats jni input jobject jrpmStats is null.");
        return;
    }
    if (jgetAndUpdatePlatformState == NULL || jgetSubsystem == NULL
            || jputVoter == NULL || jputState == NULL) {
        ALOGE("A rpmstats jni jmethodID is null.");
        return;
    }

    std::lock_guard<std::mutex> lock(gPowerHalMutex);

    if (!gGetLowPowerStatsImpl) {
        setUpPowerStatsLocked();
    }

    if (gGetLowPowerStatsImpl) {
        return gGetLowPowerStatsImpl(env, jrpmStats);
    }

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

static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
    if (outBuf == NULL) {
        jniThrowException(env, "java/lang/NullPointerException", "null argument");
        return -1;
    }

    std::lock_guard<std::mutex> lock(gPowerHalMutex);

    if (!gGetPlatformLowPowerStatsImpl) {
        setUpPowerStatsLocked();
    }

    if (gGetPlatformLowPowerStatsImpl) {
        return gGetPlatformLowPowerStatsImpl(env, outBuf);
    }

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

static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
    if (outBuf == NULL) {
        jniThrowException(env, "java/lang/NullPointerException", "null argument");
        return -1;
    }

    std::lock_guard<std::mutex> lock(gPowerHalMutex);

    if (!gGetSubsystemLowPowerStatsImpl) {
        setUpPowerStatsLocked();
    }

    if (gGetSubsystemLowPowerStatsImpl) {
        return gGetSubsystemLowPowerStatsImpl(env, outBuf);
    }

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

static const JNINativeMethod method_table[] = {
    { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
    { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },