/* * Copyright (C) 2021 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 "android.hardware.thermal@2.0-service-generic" #include #include #include #include #include #include #include #include #include "Thermal.h" #define MAX_EVENTS 10 #define SKIN_POLLING_SEC 5 // Unit is in second namespace android { namespace hardware { namespace thermal { namespace V2_0 { namespace implementation { using ::android::sp; using ::android::hardware::interfacesEqual; using ::android::hardware::thermal::V1_0::ThermalStatus; using ::android::hardware::thermal::V1_0::ThermalStatusCode; std::set> gCallbacks; Return Thermal::getCpuUsages(getCpuUsages_cb _hidl_cb) { ThermalStatus status = { .code = ThermalStatusCode::SUCCESS }; hidl_vec cpuUsages; if (m_cpuInfo.CpuUsages(cpuUsages)) _hidl_cb(status, cpuUsages); return Void(); } Return Thermal::getCoolingDevices(getCoolingDevices_cb _hidl_cb) { ThermalStatus status = { .code = ThermalStatusCode::SUCCESS }; if (this->m_config.m_cooling_device_1_0.empty()) { status.code = ThermalStatusCode::FAILURE; status.debugMessage = "No cooling devices"; } _hidl_cb(status, this->m_config.m_cooling_device_1_0); return Void(); } Return Thermal::getCurrentCoolingDevices(bool filterType, CoolingType type, getCurrentCoolingDevices_cb _hidl_cb) { ThermalStatus status = { .code = ThermalStatusCode::SUCCESS }; std::vector cooling_devices; for (auto const& p : this->m_config.m_cooling_device_2_0) { if (filterType && (type != p.type)) continue; cooling_devices.push_back(p); } if (cooling_devices.empty()) { status.code = ThermalStatusCode::FAILURE; status.debugMessage = "No cooling device matching the type"; } _hidl_cb(status, cooling_devices); return Void(); } Return Thermal::registerThermalChangedCallback(const sp& callback, bool filterType, TemperatureType type, registerThermalChangedCallback_cb _hidl_cb) { ThermalStatus status = { .code = ThermalStatusCode::SUCCESS }; std::lock_guard cbLock(m_callback_mutex); if (callback == nullptr) { status.code = ThermalStatusCode::FAILURE; status.debugMessage = "Invalid nullptr callback"; LOG(ERROR) << status.debugMessage; goto out; } if (std::any_of(m_callbacks.begin(), m_callbacks.end(), [&](const CallbackSetting& c) { return interfacesEqual(c.callback, callback); })) { status.code = ThermalStatusCode::FAILURE; status.debugMessage = "Same callback interface registered already"; LOG(ERROR) << status.debugMessage; goto out; } m_callbacks.emplace_back(callback, filterType, type); LOG(INFO) << "A callback has been registered to ThermalHAL, isFilter: " << filterType << " Type: " << toString(type); out: _hidl_cb(status); return Void(); } Return Thermal::unregisterThermalChangedCallback(const sp& callback, unregisterThermalChangedCallback_cb _hidl_cb) { ThermalStatus status = { .code = ThermalStatusCode::SUCCESS }; std::lock_guard cbLock(m_callback_mutex); std::vector::iterator it; if (callback == nullptr) { status.code = ThermalStatusCode::FAILURE; status.debugMessage = "Invalid nullptr callback"; LOG(ERROR) << status.debugMessage; goto out; } it = std::remove_if(m_callbacks.begin(), m_callbacks.end(), [&](const CallbackSetting& c) { return interfacesEqual(c.callback, callback); }); if (it == m_callbacks.end()) { status.code = ThermalStatusCode::FAILURE; status.debugMessage = "The callback was not registered before"; LOG(ERROR) << status.debugMessage; goto out; } LOG(INFO) << "A callback has been unregistered from ThermalHAL, isFilter: " << (*it).is_filter_type << " Type: " << toString((*it).type); m_callbacks.erase(it); out: _hidl_cb(status); return Void(); } void Thermal::thermalChangedCallback(const std::vector &temperature) { std::lock_guard cbLock(m_callback_mutex); for (auto &t : temperature) { LOG(INFO) << "Sending notification: " << " Type: " << android::hardware::thermal::V2_0::toString(t.type) << " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: " << android::hardware::thermal::V2_0::toString(t.throttlingStatus); for (auto &c : m_callbacks) { if (!c.is_filter_type || c.type == t.type) { c.callback->notifyThrottling(t); } } } } int Thermal::thermalZoneCreate(const char *name, int tz_id, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Thermal zone " << name << "/" << tz_id << " created"; thermal = NULL; return 0; } int Thermal::thermalZoneDelete(int tz_id, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Thermal zone " << tz_id << " delete"; thermal = NULL; return 0; } int Thermal::thermalZoneEnable(int tz_id, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Thermal zone " << tz_id << " enabled"; thermal = NULL; return 0; } int Thermal::thermalZoneDisable(int tz_id, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Thermal zone " << tz_id << " disabled"; thermal = NULL; return 0; } int Thermal::tripHigh(int tz_id, int trip_id, int temp, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Thermal zone " << tz_id << " trip crossed the way up " << "with trip_id=" << trip_id << ",temp=" << temp; temp = thermal->m_libThermal.getThermalZonetemp(tz_id); return 0; } int Thermal::tripLow(int tz_id, int trip_id, int temp, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Thermal zone " << tz_id << " trip crossed the way down " << "with trip_id=" << trip_id << ",temp=" << temp; temp = thermal->m_libThermal.getThermalZonetemp(tz_id); return 0; } int Thermal::tripAdd(int tz_id, int trip_id, int type, int temp, int hyst, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Trip " << trip_id << " from thermal zone " << tz_id << " added: type=" << type << ", temp=" << temp << " ,hyst=" << hyst; thermal = NULL; return 0; } int Thermal::tripChange(int tz_id, int trip_id, int type, int temp, int hyst, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Trip " << trip_id << " from thermal zone " << tz_id << " changed: type=" << type << ", temp=" << temp << " ,hyst=" << hyst; thermal = NULL; return 0; } int Thermal::tripDelete(int tz_id, int trip_id, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Trip " << trip_id << " from thermal zone " << tz_id << " deleted"; thermal = NULL; return 0; } int Thermal::cdevAdd(const char *name, int cdev_id, int max_state, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Cooling device '" << name << "'/" << cdev_id << " max state" << max_state << " created"; thermal = NULL; return 0; } int Thermal::cdevDelete(int cdev_id, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Cooling device " << cdev_id << " deleted"; thermal = NULL; return 0; } int Thermal::cdevUpdate(int cdev_id, int max_state, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Coolling device " << cdev_id << " max state" << max_state; thermal = NULL; return 0; } int Thermal::govChange(int tz_id, const char *name, void *arg) { class Thermal *thermal = (class Thermal *)arg; LOG(DEBUG) << "Governor change '" << name << "' " << "for thermal zone " << tz_id; thermal = NULL; return 0; } bool Thermal::skinTemperatureMonitor(class Thermal *thermal) { uint64_t expirations; if (read(thermal->m_timerfd, &expirations, sizeof(expirations)) < 0) { LOG(ERROR) << "Failed to read signal data"; return false; } for (auto const& p : thermal->m_config.m_skin_sensors) { LOG(DEBUG) << "Skin temperature for '" << p << "' " << thermal->m_libThermal.getThermalZonetemp(p) << "°C"; } return true; } Return Thermal::getTemperatureThresholds(bool filterType, TemperatureType type, getTemperatureThresholds_cb _hidl_cb) { ThermalStatus status = { .code = ThermalStatusCode::SUCCESS }; std::vector thresholds; for (auto const& p : this->m_config.m_threshold) { if (filterType && (type != p.second.type)) continue; thresholds.push_back(p.second); } if (thresholds.empty()) { status.code = ThermalStatusCode::FAILURE; status.debugMessage = "No threshold temperature matching the type"; } _hidl_cb(status, thresholds); return Void(); } // Methods from ::android::hardware::thermal::V2_0::IThermal follow. Return Thermal::getCurrentTemperatures(bool filterType, TemperatureType type, getCurrentTemperatures_cb _hidl_cb) { ThermalStatus status = { .code = ThermalStatusCode::SUCCESS }; std::vector temperatures; for (auto &p : this->m_config.m_temperature_2_0) { std::string name = p.name; int temperature; if (filterType && (type != p.type)) continue; temperature = m_libThermal.getThermalZonetemp(name); if (temperature == INT_MAX) { LOG(ERROR) << "Failed to read thermal zone temperature" << "for <" << name << ">"; continue; } p.value = temperature / 1000; temperatures.push_back(p); } if (temperatures.empty()) { status.code = ThermalStatusCode::FAILURE; status.debugMessage = "No temperature matching the type"; } _hidl_cb(status, temperatures); return Void(); } // Methods from ::android::hardware::thermal::V1_0::IThermal follow. Return Thermal::getTemperatures(getTemperatures_cb _hidl_cb) { ThermalStatus status = { . code = ThermalStatusCode::SUCCESS }; for (auto &p : this->m_config.m_temperature_1_0) { std::string name = p.name; int temperature; temperature = m_libThermal.getThermalZonetemp(name); if (temperature == INT_MAX) { LOG(ERROR) << "Failed to read thermal zone temperature" << "for <" << name << ">"; continue; } p.currentValue = temperature / 1000; } _hidl_cb(status, m_config.m_temperature_1_0); return Void(); } bool Thermal::threadLoop() { struct epoll_event ev; struct epoll_event events[MAX_EVENTS]; int epollfd; int nfds; int i; LibThermalCallbacks cb; cb.thermalZoneCreate = this->thermalZoneCreate; cb.thermalZoneDelete = this->thermalZoneDelete; cb.thermalZoneEnable = this->thermalZoneEnable; cb.thermalZoneDisable = this->thermalZoneDisable; cb.tripHigh = this->tripHigh; cb.tripLow = this->tripLow; cb.tripAdd = this->tripAdd; cb.tripChange = this->tripChange; cb.tripDelete = this->tripDelete; cb.cdevAdd = this->cdevAdd; cb.cdevDelete = this->cdevDelete; cb.cdevUpdate = this->cdevUpdate; cb.govChange = this->govChange; if (!m_config.init()) LOG(FATAL) << "ThermalHAL failed to initialize the configuration"; if (!m_libThermal.init(cb)) LOG(FATAL) << "ThermalHAL failed to initialize the thermal library"; epollfd = epoll_create1(0); if (epollfd < 0) return false; ev.events = EPOLLIN; ev.data.ptr = (void *)thermal_events_handle; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, thermal_events_fd(m_libThermal.m_th), &ev) == -1) return false; /* * We want to read the temperature every 2000ms, set a timer * file descriptor to be woken up at regular intervals. */ struct itimerspec iti = { .it_interval = { SKIN_POLLING_SEC, 0 }, .it_value = { SKIN_POLLING_SEC, 0 }, }; m_timerfd = timerfd_create(CLOCK_MONOTONIC, 0); if (m_timerfd < 0) { LOG(ERROR) << "Failed to create timer"; return false; } if (timerfd_settime(m_timerfd, 0, &iti, NULL) < 0) { LOG(ERROR) << "Failed to set timer time"; return false; } /* * There are skin temperature sensors, set a timer to poll the * temperature. */ if (!m_config.m_skin_sensors.empty()) { LOG(INFO) << "Skin sensors configured, setting up the monitoring."; ev.events = EPOLLIN; ev.data.ptr = (void *)skinTemperatureMonitor; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, m_timerfd, &ev) == -1) return false; } /* * No ops was specified for the temperature sampling, so no * event of this type should occur, but add the corresponding * code to handle it for unlikely future changes if the code * needs to have the temperature sampling. * ev.events = EPOLLIN; * ev.data.ptr = (void *)thermal_sampling_handle; * * if (epoll_ctl(epollfd, EPOLL_CTL_ADD, * thermal_sampling_fd(m_libThermal.m_th), &ev) == -1) * return false; */ while (1) { nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds < 0) { if (errno == EINTR) continue; LOG(FATAL) << "Mainloop failed with error: " << strerror(errno); } for (i = 0; i < nfds; i++) { if (events[i].data.ptr == thermal_events_handle) { thermal_events_handle(m_libThermal.m_th, NULL); } else if (events[i].data.ptr == thermal_sampling_handle) { thermal_sampling_handle(m_libThermal.m_th, NULL); } else if (events[i].data.ptr == skinTemperatureMonitor) { skinTemperatureMonitor(this); } } } return true; } Thermal::Thermal() { this->run("ThermalMonitorThread", PRIORITY_HIGHEST); } } // namespace implementation } // namespace V2_0 } // namespace thermal } // namespace hardware } // namespace android