1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <cutils/uevent.h>
17 #include <dirent.h>
18 #include <sys/inotify.h>
19 #include <sys/resource.h>
20 #include <sys/types.h>
21 #include <chrono>
22 #include <fstream>
23
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <android-base/strings.h>
27
28 #include "thermal_watcher.h"
29
30 namespace android {
31 namespace hardware {
32 namespace thermal {
33 namespace V2_0 {
34 namespace implementation {
35
36 using std::chrono_literals::operator""ms;
37
registerFilesToWatch(const std::set<std::string> & sensors_to_watch,bool uevent_monitor)38 void ThermalWatcher::registerFilesToWatch(const std::set<std::string> &sensors_to_watch,
39 bool uevent_monitor) {
40 monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
41 if (!uevent_monitor) {
42 is_polling_ = true;
43 return;
44 }
45 uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true))));
46 if (uevent_fd_.get() < 0) {
47 LOG(ERROR) << "failed to open uevent socket";
48 is_polling_ = true;
49 return;
50 }
51
52 fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
53
54 looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
55 is_polling_ = false;
56 thermal_triggered_ = true;
57 last_update_time_ = boot_clock::now();
58 }
59
startWatchingDeviceFiles()60 bool ThermalWatcher::startWatchingDeviceFiles() {
61 if (cb_) {
62 auto ret = this->run("FileWatcherThread", PRIORITY_HIGHEST);
63 if (ret != NO_ERROR) {
64 LOG(ERROR) << "ThermalWatcherThread start fail";
65 return false;
66 } else {
67 LOG(INFO) << "ThermalWatcherThread started";
68 return true;
69 }
70 }
71 return false;
72 }
parseUevent(std::set<std::string> * sensors_set)73 void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) {
74 bool thermal_event = false;
75 constexpr int kUeventMsgLen = 2048;
76 char msg[kUeventMsgLen + 2];
77 char *cp;
78
79 while (true) {
80 int n = uevent_kernel_multicast_recv(uevent_fd_.get(), msg, kUeventMsgLen);
81 if (n <= 0) {
82 if (errno != EAGAIN && errno != EWOULDBLOCK) {
83 LOG(ERROR) << "Error reading from Uevent Fd";
84 }
85 break;
86 }
87
88 if (n >= kUeventMsgLen) {
89 LOG(ERROR) << "Uevent overflowed buffer, discarding";
90 continue;
91 }
92
93 msg[n] = '\0';
94 msg[n + 1] = '\0';
95
96 cp = msg;
97 while (*cp) {
98 std::string uevent = cp;
99 if (!thermal_event) {
100 if (uevent.find("SUBSYSTEM=") == 0) {
101 if (uevent.find("SUBSYSTEM=thermal") != std::string::npos) {
102 thermal_event = true;
103 } else {
104 break;
105 }
106 }
107 } else {
108 auto start_pos = uevent.find("NAME=");
109 if (start_pos != std::string::npos) {
110 start_pos += 5;
111 std::string name = uevent.substr(start_pos);
112 if (std::find(monitored_sensors_.begin(), monitored_sensors_.end(), name) !=
113 monitored_sensors_.end()) {
114 sensors_set->insert(name);
115 }
116 break;
117 }
118 }
119 while (*cp++) {
120 }
121 }
122 }
123 }
124
wake()125 void ThermalWatcher::wake() {
126 looper_->wake();
127 }
128
threadLoop()129 bool ThermalWatcher::threadLoop() {
130 LOG(VERBOSE) << "ThermalWatcher polling...";
131 // Polling interval 2s
132 static constexpr int kMinPollIntervalMs = 2000;
133 // Max uevent timeout 5mins
134 static constexpr int kUeventPollTimeoutMs = 300000;
135 int fd;
136 std::set<std::string> sensors;
137
138 auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() -
139 last_update_time_)
140 .count();
141 int timeout = (thermal_triggered_ || is_polling_) ? kMinPollIntervalMs : kUeventPollTimeoutMs;
142 if (time_elapsed_ms < timeout && looper_->pollOnce(timeout, &fd, nullptr, nullptr) >= 0) {
143 if (fd != uevent_fd_.get()) {
144 return true;
145 }
146 parseUevent(&sensors);
147 // Ignore cb_ if uevent is not from monitored sensors
148 if (sensors.size() == 0) {
149 return true;
150 }
151 }
152 thermal_triggered_ = cb_(sensors);
153 last_update_time_ = boot_clock::now();
154 return true;
155 }
156
157 } // namespace implementation
158 } // namespace V2_0
159 } // namespace thermal
160 } // namespace hardware
161 } // namespace android
162