/* * Copyright (C) 2024 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. */ #define LOG_TAG "system_health" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "android-base/thread_annotations.h" #include "utils/SystemClock.h" using namespace android; using namespace aidl::android::os; namespace hal = aidl::android::hardware::power; struct ACpuHeadroomParams : public CpuHeadroomParamsInternal {}; struct AGpuHeadroomParams : public GpuHeadroomParamsInternal {}; struct ASystemHealthManager { public: static ASystemHealthManager* getInstance(); ASystemHealthManager(std::shared_ptr& hintManager, IHintManager::HintManagerClientData&& clientData); ASystemHealthManager() = delete; ~ASystemHealthManager(); int getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom); int getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom); int getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis); int getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis); int getMaxCpuHeadroomTidsSize(size_t* outSize); int getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, int32_t* _Nonnull outMaxMillis); int getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, int32_t* _Nonnull outMaxMillis); private: static ASystemHealthManager* create(std::shared_ptr hintManager); std::shared_ptr mHintManager; IHintManager::HintManagerClientData mClientData; }; static std::shared_ptr* gIHintManagerForTesting = nullptr; static std::shared_ptr gSystemHealthManagerForTesting = nullptr; ASystemHealthManager* ASystemHealthManager::getInstance() { static std::once_flag creationFlag; static ASystemHealthManager* instance = nullptr; if (gSystemHealthManagerForTesting) { return gSystemHealthManagerForTesting.get(); } if (gIHintManagerForTesting) { gSystemHealthManagerForTesting = std::shared_ptr(create(*gIHintManagerForTesting)); return gSystemHealthManagerForTesting.get(); } std::call_once(creationFlag, []() { instance = create(nullptr); }); return instance; } ASystemHealthManager::ASystemHealthManager(std::shared_ptr& hintManager, IHintManager::HintManagerClientData&& clientData) : mHintManager(std::move(hintManager)), mClientData(clientData) {} ASystemHealthManager::~ASystemHealthManager() = default; ASystemHealthManager* ASystemHealthManager::create(std::shared_ptr hintManager) { if (!hintManager) { hintManager = IHintManager::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService("performance_hint"))); } if (hintManager == nullptr) { ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__); return nullptr; } IHintManager::HintManagerClientData clientData; ndk::ScopedAStatus ret = hintManager->getClientData(&clientData); if (!ret.isOk()) { ALOGE("%s: PerformanceHint service is not initialized %s", __FUNCTION__, ret.getMessage()); return nullptr; } return new ASystemHealthManager(hintManager, std::move(clientData)); } int ASystemHealthManager::getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom) { if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP; std::optional res; ::ndk::ScopedAStatus ret; CpuHeadroomParamsInternal internalParams; if (!params) { ret = mHintManager->getCpuHeadroom(internalParams, &res); } else { LOG_ALWAYS_FATAL_IF((int)params->tids.size() > mClientData.maxCpuHeadroomThreads, "%s: tids size should not exceed %d", __FUNCTION__, mClientData.maxCpuHeadroomThreads); LOG_ALWAYS_FATAL_IF(params->calculationWindowMillis < mClientData.supportInfo.headroom .cpuMinCalculationWindowMillis || params->calculationWindowMillis > mClientData.supportInfo.headroom .cpuMaxCalculationWindowMillis, "%s: calculationWindowMillis should be in range [%d, %d] but got %d", __FUNCTION__, mClientData.supportInfo.headroom.cpuMinCalculationWindowMillis, mClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis, params->calculationWindowMillis); ret = mHintManager->getCpuHeadroom(*params, &res); } if (!ret.isOk()) { LOG_ALWAYS_FATAL_IF(ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT, "Invalid ACpuHeadroomParams: %s", ret.getMessage()); ALOGE("ASystemHealth_getCpuHeadroom fails: %s", ret.getMessage()); if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) { return ENOTSUP; } else if (ret.getExceptionCode() == EX_SECURITY) { return EPERM; } return EPIPE; } *outHeadroom = res ? res->get() : std::numeric_limits::quiet_NaN(); return OK; } int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom) { if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP; std::optional res; ::ndk::ScopedAStatus ret; GpuHeadroomParamsInternal internalParams; if (!params) { ret = mHintManager->getGpuHeadroom(internalParams, &res); } else { LOG_ALWAYS_FATAL_IF(params->calculationWindowMillis < mClientData.supportInfo.headroom .gpuMinCalculationWindowMillis || params->calculationWindowMillis > mClientData.supportInfo.headroom .gpuMaxCalculationWindowMillis, "%s: calculationWindowMillis should be in range [%d, %d] but got %d", __FUNCTION__, mClientData.supportInfo.headroom.gpuMinCalculationWindowMillis, mClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis, params->calculationWindowMillis); ret = mHintManager->getGpuHeadroom(*params, &res); } if (!ret.isOk()) { LOG_ALWAYS_FATAL_IF(ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT, "Invalid AGpuHeadroomParams: %s", ret.getMessage()); ALOGE("ASystemHealth_getGpuHeadroom fails: %s", ret.getMessage()); if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) { return ENOTSUP; } return EPIPE; } *outHeadroom = res ? res->get() : std::numeric_limits::quiet_NaN(); return OK; } int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) { if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP; *outMinIntervalMillis = mClientData.supportInfo.headroom.cpuMinIntervalMillis; return OK; } int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) { if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP; *outMinIntervalMillis = mClientData.supportInfo.headroom.gpuMinIntervalMillis; return OK; } int ASystemHealthManager::getMaxCpuHeadroomTidsSize(size_t* outSize) { if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP; *outSize = mClientData.maxCpuHeadroomThreads; return OK; } int ASystemHealthManager::getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, int32_t* _Nonnull outMaxMillis) { if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP; *outMinMillis = mClientData.supportInfo.headroom.cpuMinCalculationWindowMillis; *outMaxMillis = mClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis; return OK; } int ASystemHealthManager::getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, int32_t* _Nonnull outMaxMillis) { if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP; *outMinMillis = mClientData.supportInfo.headroom.gpuMinCalculationWindowMillis; *outMaxMillis = mClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis; return OK; } int ASystemHealth_getMaxCpuHeadroomTidsSize(size_t* _Nonnull outSize) { LOG_ALWAYS_FATAL_IF(outSize == nullptr, "%s: outSize should not be null", __FUNCTION__); auto manager = ASystemHealthManager::getInstance(); if (manager == nullptr) return ENOTSUP; return manager->getMaxCpuHeadroomTidsSize(outSize); } int ASystemHealth_getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, int32_t* _Nonnull outMaxMillis) { LOG_ALWAYS_FATAL_IF(outMinMillis == nullptr, "%s: outMinMillis should not be null", __FUNCTION__); LOG_ALWAYS_FATAL_IF(outMaxMillis == nullptr, "%s: outMaxMillis should not be null", __FUNCTION__); auto manager = ASystemHealthManager::getInstance(); if (manager == nullptr) return ENOTSUP; return manager->getCpuHeadroomCalculationWindowRange(outMinMillis, outMaxMillis); } int ASystemHealth_getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, int32_t* _Nonnull outMaxMillis) { LOG_ALWAYS_FATAL_IF(outMinMillis == nullptr, "%s: outMinMillis should not be null", __FUNCTION__); LOG_ALWAYS_FATAL_IF(outMaxMillis == nullptr, "%s: outMaxMillis should not be null", __FUNCTION__); auto manager = ASystemHealthManager::getInstance(); if (manager == nullptr) return ENOTSUP; return manager->getGpuHeadroomCalculationWindowRange(outMinMillis, outMaxMillis); } int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams* _Nullable params, float* _Nonnull outHeadroom) { LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__); auto manager = ASystemHealthManager::getInstance(); if (manager == nullptr) return ENOTSUP; return manager->getCpuHeadroom(params, outHeadroom); } int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams* _Nullable params, float* _Nonnull outHeadroom) { LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__); auto manager = ASystemHealthManager::getInstance(); if (manager == nullptr) return ENOTSUP; return manager->getGpuHeadroom(params, outHeadroom); } int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) { LOG_ALWAYS_FATAL_IF(outMinIntervalMillis == nullptr, "%s: outMinIntervalMillis should not be null", __FUNCTION__); auto manager = ASystemHealthManager::getInstance(); if (manager == nullptr) return ENOTSUP; return manager->getCpuHeadroomMinIntervalMillis(outMinIntervalMillis); } int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) { LOG_ALWAYS_FATAL_IF(outMinIntervalMillis == nullptr, "%s: outMinIntervalMillis should not be null", __FUNCTION__); auto manager = ASystemHealthManager::getInstance(); if (manager == nullptr) return ENOTSUP; return manager->getGpuHeadroomMinIntervalMillis(outMinIntervalMillis); } void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params, int windowMillis) { LOG_ALWAYS_FATAL_IF(windowMillis <= 0, "%s: windowMillis should be positive but got %d", __FUNCTION__, windowMillis); params->calculationWindowMillis = windowMillis; } void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params, int windowMillis) { LOG_ALWAYS_FATAL_IF(windowMillis <= 0, "%s: windowMillis should be positive but got %d", __FUNCTION__, windowMillis); params->calculationWindowMillis = windowMillis; } int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params) { return params->calculationWindowMillis; } int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params) { return params->calculationWindowMillis; } void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids, size_t tidsSize) { LOG_ALWAYS_FATAL_IF(tids == nullptr, "%s: tids should not be null", __FUNCTION__); params->tids.resize(tidsSize); for (int i = 0; i < (int)tidsSize; ++i) { LOG_ALWAYS_FATAL_IF(tids[i] <= 0, "ACpuHeadroomParams_setTids: Invalid non-positive tid %d", tids[i]); params->tids[i] = tids[i]; } } void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams* _Nonnull params, ACpuHeadroomCalculationType calculationType) { LOG_ALWAYS_FATAL_IF(calculationType < ACpuHeadroomCalculationType:: ACPU_HEADROOM_CALCULATION_TYPE_MIN || calculationType > ACpuHeadroomCalculationType:: ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE, "%s: calculationType should be one of ACpuHeadroomCalculationType values " "but got %d", __FUNCTION__, calculationType); params->calculationType = static_cast(calculationType); } ACpuHeadroomCalculationType ACpuHeadroomParams_getCalculationType( ACpuHeadroomParams* _Nonnull params) { return static_cast(params->calculationType); } void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams* _Nonnull params, AGpuHeadroomCalculationType calculationType) { LOG_ALWAYS_FATAL_IF(calculationType < AGpuHeadroomCalculationType:: AGPU_HEADROOM_CALCULATION_TYPE_MIN || calculationType > AGpuHeadroomCalculationType:: AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE, "%s: calculationType should be one of AGpuHeadroomCalculationType values " "but got %d", __FUNCTION__, calculationType); params->calculationType = static_cast(calculationType); } AGpuHeadroomCalculationType AGpuHeadroomParams_getCalculationType( AGpuHeadroomParams* _Nonnull params) { return static_cast(params->calculationType); } ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create() { return new ACpuHeadroomParams(); } AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create() { return new AGpuHeadroomParams(); } void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nullable params) { delete params; } void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nullable params) { delete params; } void ASystemHealth_setIHintManagerForTesting(void* iManager) { if (iManager == nullptr) { gSystemHealthManagerForTesting = nullptr; } gIHintManagerForTesting = static_cast*>(iManager); }