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,const std::set<std::string> & cdev_to_watch,bool uevent_monitor)38 void ThermalWatcher::registerFilesToWatch(const std::set<std::string> &sensors_to_watch,
39 const std::set<std::string> &cdev_to_watch,
40 bool uevent_monitor) {
41 int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
42
43 for (const auto &path : cdev_to_watch) {
44 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
45 if (fd == -1) {
46 PLOG(ERROR) << "failed to watch: " << path;
47 continue;
48 }
49 watch_to_file_path_map_.emplace(fd.get(), path);
50 fds_.emplace_back(std::move(fd));
51 looper_->addFd(fd.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
52 }
53 monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
54 if (!uevent_monitor) {
55 is_polling_ = true;
56 return;
57 }
58 uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true))));
59 if (uevent_fd_.get() < 0) {
60 LOG(ERROR) << "failed to open uevent socket";
61 is_polling_ = true;
62 return;
63 }
64
65 fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
66
67 looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
68 is_polling_ = false;
69 thermal_triggered_ = true;
70 }
71
startWatchingDeviceFiles()72 bool ThermalWatcher::startWatchingDeviceFiles() {
73 if (cb_) {
74 auto ret = this->run("FileWatcherThread", PRIORITY_HIGHEST);
75 if (ret != NO_ERROR) {
76 LOG(ERROR) << "ThermalWatcherThread start fail";
77 return false;
78 } else {
79 LOG(INFO) << "ThermalWatcherThread started";
80 return true;
81 }
82 }
83 return false;
84 }
parseUevent(std::set<std::string> * sensors_set)85 void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) {
86 bool thermal_event = false;
87 constexpr int kUeventMsgLen = 2048;
88 char msg[kUeventMsgLen + 2];
89 char *cp;
90
91 while (true) {
92 int n = uevent_kernel_multicast_recv(uevent_fd_.get(), msg, kUeventMsgLen);
93 if (n <= 0) {
94 if (errno != EAGAIN && errno != EWOULDBLOCK) {
95 LOG(ERROR) << "Error reading from Uevent Fd";
96 }
97 break;
98 }
99
100 if (n >= kUeventMsgLen) {
101 LOG(ERROR) << "Uevent overflowed buffer, discarding";
102 continue;
103 }
104
105 msg[n] = '\0';
106 msg[n + 1] = '\0';
107
108 cp = msg;
109 while (*cp) {
110 std::string uevent = cp;
111 if (!thermal_event) {
112 if (uevent.find("SUBSYSTEM=") == 0) {
113 if (uevent.find("SUBSYSTEM=thermal") != std::string::npos) {
114 thermal_event = true;
115 } else {
116 break;
117 }
118 }
119 } else {
120 auto start_pos = uevent.find("NAME=");
121 if (start_pos != std::string::npos) {
122 start_pos += 5;
123 std::string name = uevent.substr(start_pos);
124 if (std::find(monitored_sensors_.begin(), monitored_sensors_.end(), name) !=
125 monitored_sensors_.end()) {
126 sensors_set->insert(name);
127 }
128 break;
129 }
130 }
131 while (*cp++) {
132 }
133 }
134 }
135 }
136
wake()137 void ThermalWatcher::wake() {
138 looper_->wake();
139 }
140
threadLoop()141 bool ThermalWatcher::threadLoop() {
142 LOG(VERBOSE) << "ThermalWatcher polling...";
143 // Polling interval 2s
144 static constexpr int kMinPollIntervalMs = 2000;
145 // Max uevent timeout 5mins
146 static constexpr int kUeventPollTimeoutMs = 300000;
147 int fd;
148 std::set<std::string> sensors;
149
150 int timeout = (thermal_triggered_ || is_polling_) ? kMinPollIntervalMs : kUeventPollTimeoutMs;
151 if (looper_->pollOnce(timeout, &fd, nullptr, nullptr) >= 0) {
152 if (fd != uevent_fd_.get()) {
153 return true;
154 }
155 parseUevent(&sensors);
156 // Ignore cb_ if uevent is not from monitored sensors
157 if (sensors.size() == 0) {
158 return true;
159 }
160 }
161 thermal_triggered_ = cb_(sensors);
162 return true;
163 }
164
165 } // namespace implementation
166 } // namespace V2_0
167 } // namespace thermal
168 } // namespace hardware
169 } // namespace android
170