blob: b43f2f16a7cb599e79c4e2b863ed6c9264d8fe08 [file] [log] [blame]
/*
* 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 <android-base/thread_annotations.h>
#include <android/os/BnThermalStatusListener.h>
#include <android/os/IThermalService.h>
#include <android/thermal.h>
#include <binder/IServiceManager.h>
#include <thermal_private.h>
#include <utils/Log.h>
#include <cerrno>
#include <limits>
#include <thread>
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<IThermalService> service);
sp<IThermalService> mThermalSvc;
std::mutex mListenerMutex;
sp<ThermalServiceListener> mServiceListener GUARDED_BY(mListenerMutex);
std::vector<ListenerCallback> 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<IBinder> binder =
defaultServiceManager()->checkService(String16("thermalservice"));
if (binder == nullptr) {
ALOGE("%s: Thermal service is not ready ", __FUNCTION__);
return nullptr;
}
return new AThermalManager(interface_cast<IThermalService>(binder));
}
AThermalManager::AThermalManager(sp<IThermalService> service)
: mThermalSvc(std::move(service)), mServiceListener(nullptr) {}
AThermalManager::~AThermalManager() {
std::unique_lock<std::mutex> listenerLock(mListenerMutex);
mListeners.clear();
if (mServiceListener != nullptr) {
bool success = false;
mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
mServiceListener = nullptr;
}
listenerLock.unlock();
std::unique_lock<std::mutex> lock(mThresholdsMutex);
delete[] mThresholds;
}
status_t AThermalManager::notifyStateChange(int32_t status) {
std::unique_lock<std::mutex> lock(mListenerMutex);
AThermalStatus thermalStatus = static_cast<AThermalStatus>(status);
for (auto listener : mListeners) {
listener.callback(listener.data, thermalStatus);
}
return OK;
}
status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) {
std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> lock(mThresholdsMutex);
if (mThresholds == nullptr) {
auto thresholds = std::make_unique<std::vector<float>>();
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<AThermalStatus>(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<AThermalStatus>(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<float>::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 *>(iThermalService);
}