| /* |
| * 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 <aidl/android/hardware/power/CpuHeadroomParams.h> |
| #include <aidl/android/hardware/power/GpuHeadroomParams.h> |
| #include <aidl/android/os/CpuHeadroomParamsInternal.h> |
| #include <aidl/android/os/GpuHeadroomParamsInternal.h> |
| #include <aidl/android/os/IHintManager.h> |
| #include <android/binder_manager.h> |
| #include <android/system_health.h> |
| #include <binder/IServiceManager.h> |
| #include <binder/Status.h> |
| #include <system_health_private.h> |
| |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <mutex> |
| #include <optional> |
| #include <utility> |
| |
| #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<IHintManager>& 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<IHintManager> hintManager); |
| std::shared_ptr<IHintManager> mHintManager; |
| IHintManager::HintManagerClientData mClientData; |
| }; |
| |
| static std::shared_ptr<IHintManager>* gIHintManagerForTesting = nullptr; |
| static std::shared_ptr<ASystemHealthManager> 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<ASystemHealthManager>(create(*gIHintManagerForTesting)); |
| return gSystemHealthManagerForTesting.get(); |
| } |
| std::call_once(creationFlag, []() { instance = create(nullptr); }); |
| return instance; |
| } |
| |
| ASystemHealthManager::ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager, |
| IHintManager::HintManagerClientData&& clientData) |
| : mHintManager(std::move(hintManager)), mClientData(clientData) {} |
| |
| ASystemHealthManager::~ASystemHealthManager() = default; |
| |
| ASystemHealthManager* ASystemHealthManager::create(std::shared_ptr<IHintManager> 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<hal::CpuHeadroomResult> 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<hal::CpuHeadroomResult::Tag::globalHeadroom>() |
| : std::numeric_limits<float>::quiet_NaN(); |
| return OK; |
| } |
| |
| int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom) { |
| if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP; |
| std::optional<hal::GpuHeadroomResult> 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<hal::GpuHeadroomResult::Tag::globalHeadroom>() |
| : std::numeric_limits<float>::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<hal::CpuHeadroomParams::CalculationType>(calculationType); |
| } |
| |
| ACpuHeadroomCalculationType ACpuHeadroomParams_getCalculationType( |
| ACpuHeadroomParams* _Nonnull params) { |
| return static_cast<ACpuHeadroomCalculationType>(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<hal::GpuHeadroomParams::CalculationType>(calculationType); |
| } |
| |
| AGpuHeadroomCalculationType AGpuHeadroomParams_getCalculationType( |
| AGpuHeadroomParams* _Nonnull params) { |
| return static_cast<AGpuHeadroomCalculationType>(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<std::shared_ptr<IHintManager>*>(iManager); |
| } |