/* * Copyright (C) 2020 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 "thermal" #include #include #include #include #include #include #include #include #include #include #include using android::sp; using namespace android; using namespace android::os; struct ThermalServiceStatusListener : public BnThermalStatusListener { public: virtual binder::Status onStatusChange(int32_t status) override; ThermalServiceStatusListener(AThermalManager *manager) { mMgr = manager; } private: AThermalManager *mMgr; }; struct ThermalServiceHeadroomListener : public BnThermalHeadroomListener { public: virtual binder::Status onHeadroomChange(float headroom, float forecastHeadroom, int32_t forecastSeconds, const ::std::vector &thresholds) override; ThermalServiceHeadroomListener(AThermalManager *manager) { mMgr = manager; } private: AThermalManager *mMgr; }; struct StatusListenerCallback { AThermal_StatusCallback callback; void* data; }; struct HeadroomListenerCallback { AThermal_HeadroomCallback callback; void *data; }; static IThermalService *gIThermalServiceForTesting = nullptr; struct AThermalManager { public: static AThermalManager *createAThermalManager(); AThermalManager() = delete; ~AThermalManager(); status_t notifyStateChange(int32_t status); status_t notifyHeadroomChange(float headroom, float forecastHeadroom, int32_t forecastSeconds, const ::std::vector &thresholds); status_t getCurrentThermalStatus(int32_t *status); status_t addStatusListener(AThermal_StatusCallback, void *data); status_t removeStatusListener(AThermal_StatusCallback, void *data); status_t getThermalHeadroom(int32_t forecastSeconds, float *result); status_t getThermalHeadroomThresholds(const AThermalHeadroomThreshold **, size_t *size); status_t addHeadroomListener(AThermal_HeadroomCallback, void *data); status_t removeHeadroomListener(AThermal_HeadroomCallback, void *data); private: AThermalManager(sp service); sp mThermalSvc; std::mutex mStatusListenerMutex; sp mServiceStatusListener GUARDED_BY(mStatusListenerMutex); std::vector mStatusListeners GUARDED_BY(mStatusListenerMutex); std::mutex mHeadroomListenerMutex; sp mServiceHeadroomListener GUARDED_BY(mHeadroomListenerMutex); std::vector mHeadroomListeners GUARDED_BY(mHeadroomListenerMutex); }; binder::Status ThermalServiceStatusListener::onStatusChange(int32_t status) { if (mMgr != nullptr) { mMgr->notifyStateChange(status); } return binder::Status::ok(); } binder::Status ThermalServiceHeadroomListener::onHeadroomChange( float headroom, float forecastHeadroom, int32_t forecastSeconds, const ::std::vector &thresholds) { if (mMgr != nullptr) { mMgr->notifyHeadroomChange(headroom, forecastHeadroom, forecastSeconds, thresholds); } return binder::Status::ok(); } AThermalManager* AThermalManager::createAThermalManager() { if (gIThermalServiceForTesting) { return new AThermalManager(gIThermalServiceForTesting); } sp binder = defaultServiceManager()->checkService(String16("thermalservice")); if (binder == nullptr) { ALOGE("%s: Thermal service is not ready ", __FUNCTION__); return nullptr; } return new AThermalManager(interface_cast(binder)); } AThermalManager::AThermalManager(sp service) : mThermalSvc(std::move(service)), mServiceStatusListener(nullptr), mServiceHeadroomListener(nullptr) {} AThermalManager::~AThermalManager() { { std::scoped_lock listenerLock(mStatusListenerMutex); mStatusListeners.clear(); if (mServiceStatusListener != nullptr) { bool success = false; mThermalSvc->unregisterThermalStatusListener(mServiceStatusListener, &success); mServiceStatusListener = nullptr; } } { std::scoped_lock headroomListenerLock(mHeadroomListenerMutex); mHeadroomListeners.clear(); if (mServiceHeadroomListener != nullptr) { bool success = false; mThermalSvc->unregisterThermalHeadroomListener(mServiceHeadroomListener, &success); mServiceHeadroomListener = nullptr; } } } status_t AThermalManager::notifyStateChange(int32_t status) { std::scoped_lock lock(mStatusListenerMutex); AThermalStatus thermalStatus = static_cast(status); for (auto listener : mStatusListeners) { listener.callback(listener.data, thermalStatus); } return OK; } status_t AThermalManager::notifyHeadroomChange(float headroom, float forecastHeadroom, int32_t forecastSeconds, const ::std::vector &thresholds) { std::scoped_lock lock(mHeadroomListenerMutex); size_t thresholdsCount = thresholds.size(); auto t = new AThermalHeadroomThreshold[thresholdsCount]; for (int i = 0; i < (int)thresholdsCount; i++) { t[i].headroom = thresholds[i]; t[i].thermalStatus = static_cast(i); } for (auto listener : mHeadroomListeners) { listener.callback(listener.data, headroom, forecastHeadroom, forecastSeconds, t, thresholdsCount); } delete[] t; return OK; } status_t AThermalManager::addStatusListener(AThermal_StatusCallback callback, void *data) { std::scoped_lock lock(mStatusListenerMutex); if (callback == nullptr) { // Callback can not be nullptr return EINVAL; } for (const auto &cb : mStatusListeners) { // Don't re-add callbacks. if (callback == cb.callback && data == cb.data) { return EINVAL; } } if (mServiceStatusListener != nullptr) { mStatusListeners.emplace_back(StatusListenerCallback{callback, data}); return OK; } bool success = false; mServiceStatusListener = new ThermalServiceStatusListener(this); if (mServiceStatusListener == nullptr) { return ENOMEM; } auto ret = mThermalSvc->registerThermalStatusListener(mServiceStatusListener, &success); if (!success || !ret.isOk()) { mServiceStatusListener = nullptr; ALOGE("Failed in registerThermalStatusListener %d", success); if (ret.exceptionCode() == binder::Status::EX_SECURITY) { return EPERM; } return EPIPE; } mStatusListeners.emplace_back(StatusListenerCallback{callback, data}); return OK; } status_t AThermalManager::removeStatusListener(AThermal_StatusCallback callback, void *data) { std::scoped_lock lock(mStatusListenerMutex); auto it = std::remove_if(mStatusListeners.begin(), mStatusListeners.end(), [&](const StatusListenerCallback &cb) { return callback == cb.callback && data == cb.data; }); if (it == mStatusListeners.end()) { // If the listener and data pointer were not previously added. return EINVAL; } if (mServiceStatusListener == nullptr || mStatusListeners.size() > 1) { mStatusListeners.erase(it, mStatusListeners.end()); return OK; } bool success = false; auto ret = mThermalSvc->unregisterThermalStatusListener(mServiceStatusListener, &success); if (!success || !ret.isOk()) { ALOGE("Failed in unregisterThermalStatusListener %d", success); if (ret.exceptionCode() == binder::Status::EX_SECURITY) { return EPERM; } return EPIPE; } mServiceStatusListener = nullptr; mStatusListeners.erase(it, mStatusListeners.end()); return OK; } status_t AThermalManager::addHeadroomListener(AThermal_HeadroomCallback callback, void *data) { std::scoped_lock lock(mHeadroomListenerMutex); if (callback == nullptr) { return EINVAL; } for (const auto &cb : mHeadroomListeners) { if (callback == cb.callback && data == cb.data) { return EINVAL; } } if (mServiceHeadroomListener != nullptr) { mHeadroomListeners.emplace_back(HeadroomListenerCallback{callback, data}); return OK; } bool success = false; mServiceHeadroomListener = new ThermalServiceHeadroomListener(this); if (mServiceHeadroomListener == nullptr) { return ENOMEM; } auto ret = mThermalSvc->registerThermalHeadroomListener(mServiceHeadroomListener, &success); if (!success || !ret.isOk()) { ALOGE("Failed in registerThermalHeadroomListener %d", success); mServiceHeadroomListener = nullptr; if (ret.exceptionCode() == binder::Status::EX_SECURITY) { return EPERM; } return EPIPE; } mHeadroomListeners.emplace_back(HeadroomListenerCallback{callback, data}); return OK; } status_t AThermalManager::removeHeadroomListener(AThermal_HeadroomCallback callback, void *data) { std::scoped_lock lock(mHeadroomListenerMutex); auto it = std::remove_if(mHeadroomListeners.begin(), mHeadroomListeners.end(), [&](const HeadroomListenerCallback &cb) { return callback == cb.callback && data == cb.data; }); if (it == mHeadroomListeners.end()) { return EINVAL; } if (mServiceHeadroomListener == nullptr || mHeadroomListeners.size() > 1) { mHeadroomListeners.erase(it, mHeadroomListeners.end()); return OK; } bool success = false; auto ret = mThermalSvc->unregisterThermalHeadroomListener(mServiceHeadroomListener, &success); if (!success || !ret.isOk()) { ALOGE("Failed in unregisterThermalHeadroomListener %d", success); if (ret.exceptionCode() == binder::Status::EX_SECURITY) { return EPERM; } return EPIPE; } mServiceHeadroomListener = nullptr; mHeadroomListeners.erase(it, mHeadroomListeners.end()); return OK; } status_t AThermalManager::getCurrentThermalStatus(int32_t *status) { binder::Status ret = mThermalSvc->getCurrentThermalStatus(status); if (!ret.isOk()) { if (ret.exceptionCode() == binder::Status::EX_SECURITY) { return EPERM; } return EPIPE; } return OK; } status_t AThermalManager::getThermalHeadroom(int32_t forecastSeconds, float *result) { binder::Status ret = mThermalSvc->getThermalHeadroom(forecastSeconds, result); if (!ret.isOk()) { if (ret.exceptionCode() == binder::Status::EX_SECURITY) { return EPERM; } return EPIPE; } return OK; } status_t AThermalManager::getThermalHeadroomThresholds(const AThermalHeadroomThreshold **result, size_t *size) { auto thresholds = std::make_unique>(); binder::Status ret = mThermalSvc->getThermalHeadroomThresholds(thresholds.get()); if (!ret.isOk()) { if (ret.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { // feature is not enabled return ENOSYS; } return EPIPE; } size_t thresholdsCount = thresholds->size(); auto t = new AThermalHeadroomThreshold[thresholdsCount]; for (int i = 0; i < (int)thresholdsCount; i++) { t[i].headroom = (*thresholds)[i]; t[i].thermalStatus = static_cast(i); } *size = thresholdsCount; *result = t; return OK; } AThermalManager* AThermal_acquireManager() { auto manager = AThermalManager::createAThermalManager(); return manager; } void AThermal_releaseManager(AThermalManager *manager) { delete manager; } AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) { int32_t status = 0; status_t ret = manager->getCurrentThermalStatus(&status); if (ret != OK) { return AThermalStatus::ATHERMAL_STATUS_ERROR; } return static_cast(status); } int AThermal_registerThermalStatusListener(AThermalManager *manager, AThermal_StatusCallback callback, void *data) { return manager->addStatusListener(callback, data); } int AThermal_unregisterThermalStatusListener(AThermalManager *manager, AThermal_StatusCallback callback, void *data) { return manager->removeStatusListener(callback, data); } float AThermal_getThermalHeadroom(AThermalManager *manager, int forecastSeconds) { float result = 0.0f; status_t ret = manager->getThermalHeadroom(forecastSeconds, &result); if (ret != OK) { result = std::numeric_limits::quiet_NaN(); } return result; } int AThermal_getThermalHeadroomThresholds(AThermalManager *manager, const AThermalHeadroomThreshold **outThresholds, size_t *size) { if (outThresholds == nullptr || *outThresholds != nullptr || size == nullptr) { return EINVAL; } return manager->getThermalHeadroomThresholds(outThresholds, size); } void AThermal_setIThermalServiceForTesting(void *iThermalService) { gIThermalServiceForTesting = static_cast(iThermalService); } int AThermal_registerThermalHeadroomListener(AThermalManager *manager, AThermal_HeadroomCallback callback, void *data) { return manager->addHeadroomListener(callback, data); } int AThermal_unregisterThermalHeadroomListener(AThermalManager *manager, AThermal_HeadroomCallback callback, void *data) { return manager->removeHeadroomListener(callback, data); }