/* * 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 using android::sp; using namespace android; using namespace android::os; struct ThermalServiceListener : public BnThermalStatusListener { public: virtual binder::Status onStatusChange(int32_t status) override; ThermalServiceListener(AThermalManager *manager) { mMgr = manager; } private: AThermalManager *mMgr; }; struct ListenerCallback { AThermal_StatusCallback callback; void* data; }; static IThermalService *gIThermalServiceForTesting = nullptr; struct AThermalManager { public: static AThermalManager *createAThermalManager(); AThermalManager() = delete; ~AThermalManager(); status_t notifyStateChange(int32_t status); status_t getCurrentThermalStatus(int32_t *status); status_t addListener(AThermal_StatusCallback, void *data); status_t removeListener(AThermal_StatusCallback, void *data); status_t getThermalHeadroom(int32_t forecastSeconds, float *result); status_t getThermalHeadroomThresholds(const AThermalHeadroomThreshold **, size_t *size); private: AThermalManager(sp service); sp mThermalSvc; std::mutex mListenerMutex; sp mServiceListener GUARDED_BY(mListenerMutex); std::vector mListeners GUARDED_BY(mListenerMutex); std::mutex mThresholdsMutex; const AThermalHeadroomThreshold *mThresholds = nullptr; // GUARDED_BY(mThresholdsMutex) size_t mThresholdsCount GUARDED_BY(mThresholdsMutex); }; binder::Status ThermalServiceListener::onStatusChange(int32_t status) { if (mMgr != nullptr) { mMgr->notifyStateChange(status); } 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)), mServiceListener(nullptr) {} AThermalManager::~AThermalManager() { std::unique_lock listenerLock(mListenerMutex); mListeners.clear(); if (mServiceListener != nullptr) { bool success = false; mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); mServiceListener = nullptr; } listenerLock.unlock(); std::unique_lock lock(mThresholdsMutex); delete[] mThresholds; } status_t AThermalManager::notifyStateChange(int32_t status) { std::unique_lock lock(mListenerMutex); AThermalStatus thermalStatus = static_cast(status); for (auto listener : mListeners) { listener.callback(listener.data, thermalStatus); } return OK; } status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) { std::unique_lock lock(mListenerMutex); if (callback == nullptr) { // Callback can not be nullptr return EINVAL; } for (const auto& cb : mListeners) { // Don't re-add callbacks. if (callback == cb.callback && data == cb.data) { return EINVAL; } } mListeners.emplace_back(ListenerCallback{callback, data}); if (mServiceListener != nullptr) { return OK; } bool success = false; mServiceListener = new ThermalServiceListener(this); if (mServiceListener == nullptr) { return ENOMEM; } auto ret = mThermalSvc->registerThermalStatusListener(mServiceListener, &success); if (!success || !ret.isOk()) { ALOGE("Failed in registerThermalStatusListener %d", success); if (ret.exceptionCode() == binder::Status::EX_SECURITY) { return EPERM; } return EPIPE; } return OK; } status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) { std::unique_lock lock(mListenerMutex); auto it = std::remove_if(mListeners.begin(), mListeners.end(), [&](const ListenerCallback& cb) { return callback == cb.callback && data == cb.data; }); if (it == mListeners.end()) { // If the listener and data pointer were not previously added. return EINVAL; } mListeners.erase(it, mListeners.end()); if (!mListeners.empty()) { return OK; } if (mServiceListener == nullptr) { return OK; } bool success = false; auto ret = mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); if (!success || !ret.isOk()) { ALOGE("Failed in unregisterThermalStatusListener %d", success); if (ret.exceptionCode() == binder::Status::EX_SECURITY) { return EPERM; } return EPIPE; } mServiceListener = nullptr; 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) { std::unique_lock lock(mThresholdsMutex); if (mThresholds == nullptr) { 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; } mThresholdsCount = thresholds->size(); auto t = new AThermalHeadroomThreshold[mThresholdsCount]; for (int i = 0; i < (int)mThresholdsCount; i++) { t[i].headroom = (*thresholds)[i]; t[i].thermalStatus = static_cast(i); } mThresholds = t; } *size = mThresholdsCount; *result = mThresholds; return OK; } /** * Acquire an instance of the thermal manager. This must be freed using * {@link AThermal_releaseManager}. * * @return manager instance on success, nullptr on failure. */ AThermalManager* AThermal_acquireManager() { auto manager = AThermalManager::createAThermalManager(); return manager; } /** * Release the thermal manager pointer acquired by * {@link AThermal_acquireManager}. * * @param manager The manager to be released. * */ void AThermal_releaseManager(AThermalManager *manager) { delete manager; } /** * Gets the current thermal status. * * @param manager The manager instance to use to query the thermal status, * acquired by {@link AThermal_acquireManager}. * * @return current thermal status, ATHERMAL_STATUS_ERROR on failure. */ 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); } /** * Register the thermal status listener for thermal status change. * * @param manager The manager instance to use to register. * acquired by {@link AThermal_acquireManager}. * @param callback The callback function to be called when thermal status updated. * @param data The data pointer to be passed when callback is called. * * @return 0 on success * EINVAL if the listener and data pointer were previously added and not removed. * EPERM if the required permission is not held. * EPIPE if communication with the system service has failed. */ int AThermal_registerThermalStatusListener(AThermalManager *manager, AThermal_StatusCallback callback, void *data) { return manager->addListener(callback, data); } /** * Unregister the thermal status listener previously resgistered. * * @param manager The manager instance to use to unregister. * acquired by {@link AThermal_acquireManager}. * @param callback The callback function to be called when thermal status updated. * @param data The data pointer to be passed when callback is called. * * @return 0 on success * EINVAL if the listener and data pointer were not previously added. * EPERM if the required permission is not held. * EPIPE if communication with the system service has failed. */ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, AThermal_StatusCallback callback, void *data) { return manager->removeListener(callback, data); } /** * Provides an estimate of how much thermal headroom the device currently has * before hitting severe throttling. * * Note that this only attempts to track the headroom of slow-moving sensors, * such as the skin temperature sensor. This means that there is no benefit to * calling this function more frequently than about once per second, and attempts * to call significantly more frequently may result in the function returning {@code NaN}. * * See also PowerManager#getThermalHeadroom. * * @param manager The manager instance to use * @param forecastSeconds how many seconds in the future to forecast * @return a value greater than or equal to 0.0 where 1.0 indicates the SEVERE throttling * threshold. Returns NaN if the device does not support this functionality or if * this function is called significantly faster than once per second. */ 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); }