310 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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 "libPixelUsbOverheat"
 | |
| 
 | |
| #include "include/pixelusb/UsbOverheatEvent.h"
 | |
| 
 | |
| #include <time.h>
 | |
| 
 | |
| namespace android {
 | |
| namespace hardware {
 | |
| namespace google {
 | |
| namespace pixel {
 | |
| namespace usb {
 | |
| 
 | |
| // Start monitoring the temperature
 | |
| static volatile bool monitorTemperature;
 | |
| 
 | |
| constexpr int kEpollEvents = 10;
 | |
| constexpr char kOverheatLock[] = "overheat";
 | |
| constexpr char kWakeLockPath[] = "/sys/power/wake_lock";
 | |
| constexpr char kWakeUnlockPath[] = "/sys/power/wake_unlock";
 | |
| 
 | |
| int addEpollFdWakeUp(const unique_fd &epfd, const unique_fd &fd) {
 | |
|     struct epoll_event event;
 | |
|     int ret;
 | |
| 
 | |
|     event.data.fd = fd;
 | |
|     event.events = EPOLLIN | EPOLLWAKEUP;
 | |
| 
 | |
|     ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
 | |
|     if (ret)
 | |
|         ALOGE("epoll_ctl error %d", errno);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| UsbOverheatEvent::UsbOverheatEvent(const ZoneInfo &monitored_zone,
 | |
|                                    const std::vector<ZoneInfo> &queried_zones,
 | |
|                                    const int &monitor_interval_sec)
 | |
|     : monitored_zone_(monitored_zone),
 | |
|       queried_zones_(queried_zones),
 | |
|       monitor_interval_sec_(monitor_interval_sec),
 | |
|       monitor_() {
 | |
|     int fd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0);
 | |
|     if (fd < 0) {
 | |
|         ALOGE("timerfd_create failed: %d", errno);
 | |
|     }
 | |
| 
 | |
|     unique_fd timerFd(timerfd_create(CLOCK_BOOTTIME_ALARM, 0));
 | |
|     if (timerFd == -1) {
 | |
|         ALOGE("timerFd failed to create %d", errno);
 | |
|         abort();
 | |
|     }
 | |
| 
 | |
|     unique_fd epollFd(epoll_create(2));
 | |
|     if (epollFd == -1) {
 | |
|         ALOGE("epoll_fd_ failed to create %d", errno);
 | |
|         abort();
 | |
|     }
 | |
| 
 | |
|     unique_fd eventFd(eventfd(0, 0));
 | |
|     if (eventFd == -1) {
 | |
|         ALOGE("event_fd_ failed to create %d", errno);
 | |
|         abort();
 | |
|     }
 | |
| 
 | |
|     if (addEpollFdWakeUp(epollFd, timerFd) == -1) {
 | |
|         ALOGE("Adding timerFd failed");
 | |
|         abort();
 | |
|     }
 | |
| 
 | |
|     if (addEpollFdWakeUp(epollFd, eventFd) == -1) {
 | |
|         ALOGE("Adding eventFd failed");
 | |
|         abort();
 | |
|     }
 | |
| 
 | |
|     epoll_fd_ = move(epollFd);
 | |
|     timer_fd_ = move(timerFd);
 | |
|     event_fd_ = move(eventFd);
 | |
| 
 | |
|     monitor_ = unique_ptr<thread>(new thread(this->monitorThread, this));
 | |
|     registerListener();
 | |
| }
 | |
| 
 | |
| static int wakelock_cnt = 0;
 | |
| static std::mutex wakelock_lock;
 | |
| 
 | |
| static void wakeLockAcquire() {
 | |
|     lock_guard<mutex> lock(wakelock_lock);
 | |
| 
 | |
|     wakelock_cnt++;
 | |
|     if (wakelock_cnt == 1) {
 | |
|         ALOGV("Acquire wakelock");
 | |
|         if (!WriteStringToFile(kOverheatLock, kWakeLockPath)) {
 | |
|             ALOGE("Failed to acquire wake lock string");
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void wakeLockRelease() {
 | |
|     lock_guard<mutex> lock(wakelock_lock);
 | |
| 
 | |
|     wakelock_cnt--;
 | |
|     if (wakelock_cnt == 0) {
 | |
|         ALOGV("Release wakelock");
 | |
|         if (!WriteStringToFile(kOverheatLock, kWakeUnlockPath)) {
 | |
|             ALOGE("Failed to acquire wake lock string");
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void *UsbOverheatEvent::monitorThread(void *param) {
 | |
|     UsbOverheatEvent *overheatEvent = (UsbOverheatEvent *)param;
 | |
|     struct epoll_event events[kEpollEvents];
 | |
|     struct itimerspec delay = itimerspec();
 | |
| 
 | |
|     while (true) {
 | |
|         uint64_t fired;
 | |
|         float temperature = 0;
 | |
|         string status;
 | |
| 
 | |
|         for (vector<ZoneInfo>::size_type i = 0; i < overheatEvent->queried_zones_.size(); i++) {
 | |
|             if (overheatEvent->getCurrentTemperature(overheatEvent->queried_zones_[i].name_,
 | |
|                                                      &temperature)) {
 | |
|                 if (i == 0)
 | |
|                     overheatEvent->max_overheat_temp_ =
 | |
|                             max(temperature, overheatEvent->max_overheat_temp_);
 | |
|                 status.append(overheatEvent->queried_zones_[i].name_);
 | |
|                 status.append(":");
 | |
|                 status.append(std::to_string(temperature));
 | |
|                 status.append(" ");
 | |
|             }
 | |
|         }
 | |
|         ALOGW("%s", status.c_str());
 | |
| 
 | |
|         delay.it_value.tv_sec = monitorTemperature ? overheatEvent->monitor_interval_sec_ : 0;
 | |
|         int ret = timerfd_settime(overheatEvent->timer_fd_, 0, &delay, NULL);
 | |
|         if (ret < 0) {
 | |
|             ALOGE("timerfd_settime failed. err:%d tv_sec:%ld", errno, delay.it_value.tv_sec);
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         wakeLockRelease();
 | |
|         int nrEvents = epoll_wait(overheatEvent->epoll_fd_, events, kEpollEvents, -1);
 | |
|         wakeLockAcquire();
 | |
|         if (nrEvents <= 0) {
 | |
|             ALOGE("nrEvents negative skipping");
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         for (int i = 0; i < nrEvents; i++) {
 | |
|             ALOGV("event=%u on fd=%d\n", events[i].events, events[i].data.fd);
 | |
| 
 | |
|             if (events[i].data.fd == overheatEvent->timer_fd_) {
 | |
|                 ALOGI("Wake up caused by timer fd");
 | |
|                 int numRead = read(overheatEvent->timer_fd_, &fired, sizeof(fired));
 | |
|                 if (numRead != sizeof(fired)) {
 | |
|                     ALOGV("numRead incorrect");
 | |
|                 }
 | |
|                 if (fired != 1) {
 | |
|                     ALOGV("Fired not set to 1");
 | |
|                 }
 | |
|             } else {
 | |
|                 ALOGI("Wake up caused by event fd");
 | |
|                 read(overheatEvent->event_fd_, &fired, sizeof(fired));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| bool UsbOverheatEvent::registerListener() {
 | |
|     ALOGV("UsbOverheatEvent::registerListener");
 | |
|     sp<IServiceManager> sm = IServiceManager::getService();
 | |
|     if (sm == NULL) {
 | |
|         ALOGE("Hardware service manager is not running");
 | |
|         return false;
 | |
|     }
 | |
|     Return<bool> result = sm->registerForNotifications(IThermal::descriptor, "", this);
 | |
|     if (result.isOk()) {
 | |
|         return true;
 | |
|     }
 | |
|     ALOGE("Failed to register for hardware service manager notifications: %s",
 | |
|           result.description().c_str());
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| void UsbOverheatEvent::wakeupMonitor() {
 | |
|     // <flag> value does not have any significance here
 | |
|     uint64_t flag = 100;
 | |
| 
 | |
|     unsigned long ret = TEMP_FAILURE_RETRY(write(event_fd_, &flag, sizeof(flag)));
 | |
|     if (ret < 0) {
 | |
|         ALOGE("Error writing eventfd errno=%d", errno);
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool UsbOverheatEvent::startRecording() {
 | |
|     ALOGI("Start recording. monitorTemperature:%d", monitorTemperature ? 1 : 0);
 | |
|     // Bail out if temperature was being monitored previously
 | |
|     if (monitorTemperature)
 | |
|         return true;
 | |
| 
 | |
|     wakeLockAcquire();
 | |
|     monitorTemperature = true;
 | |
|     wakeupMonitor();
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| bool UsbOverheatEvent::stopRecording() {
 | |
|     ALOGI("Stop recording. monitorTemperature:%d", monitorTemperature ? 1 : 0);
 | |
|     // Bail out if temperature was not being monitored previously
 | |
|     if (!monitorTemperature)
 | |
|         return true;
 | |
| 
 | |
|     wakeLockRelease();
 | |
|     monitorTemperature = false;
 | |
|     wakeupMonitor();
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| bool UsbOverheatEvent::getCurrentTemperature(const string &name, float *temp) {
 | |
|     ThermalStatus thermal_status;
 | |
|     hidl_vec<Temperature> thermal_temperatures;
 | |
| 
 | |
|     if (thermal_service_ == NULL)
 | |
|         return false;
 | |
| 
 | |
|     auto ret = thermal_service_->getCurrentTemperatures(
 | |
|             false, TemperatureType::USB_PORT,
 | |
|             [&](ThermalStatus status, hidl_vec<Temperature> temperatures) {
 | |
|                 thermal_status = status;
 | |
|                 thermal_temperatures = temperatures;
 | |
|             });
 | |
| 
 | |
|     if (ret.isOk() && thermal_status.code == ThermalStatusCode::SUCCESS) {
 | |
|         for (auto temperature : thermal_temperatures) {
 | |
|             if (temperature.name == name) {
 | |
|                 *temp = temperature.value;
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| float UsbOverheatEvent::getMaxOverheatTemperature() {
 | |
|     return max_overheat_temp_;
 | |
| }
 | |
| 
 | |
| Return<void> UsbOverheatEvent::onRegistration(const hidl_string & /*fully_qualified_name*/,
 | |
|                                               const hidl_string & /*instance_name*/,
 | |
|                                               bool /*pre_existing*/) {
 | |
|     ThermalStatus thermal_status;
 | |
| 
 | |
|     thermal_service_ = IThermal::getService();
 | |
|     if (thermal_service_ == NULL) {
 | |
|         ALOGE("Unable to get Themal Service");
 | |
|         return Void();
 | |
|     }
 | |
| 
 | |
|     auto ret = thermal_service_->registerThermalChangedCallback(
 | |
|             this, true, monitored_zone_.type_,
 | |
|             [&](ThermalStatus status) { thermal_status = status; });
 | |
| 
 | |
|     if (!ret.isOk() || thermal_status.code != ThermalStatusCode::SUCCESS) {
 | |
|         ALOGE("failed to register thermal changed callback!");
 | |
|     }
 | |
| 
 | |
|     return Void();
 | |
| }
 | |
| 
 | |
| Return<void> UsbOverheatEvent::notifyThrottling(const Temperature &temperature) {
 | |
|     ALOGV("notifyThrottling '%s' T=%2.2f throttlingStatus=%d", temperature.name.c_str(),
 | |
|           temperature.value, temperature.throttlingStatus);
 | |
|     if (temperature.type == monitored_zone_.type_) {
 | |
|         if (temperature.throttlingStatus >= monitored_zone_.severity_) {
 | |
|             startRecording();
 | |
|         } else {
 | |
|             stopRecording();
 | |
|         }
 | |
|     }
 | |
|     return Void();
 | |
| }
 | |
| 
 | |
| ZoneInfo::ZoneInfo(const TemperatureType &type, const string &name,
 | |
|                    const ThrottlingSeverity &severity)
 | |
|     : type_(type), name_(name), severity_(severity) {}
 | |
| }  // namespace usb
 | |
| }  // namespace pixel
 | |
| }  // namespace google
 | |
| }  // namespace hardware
 | |
| }  // namespace android
 |