system/core
リビジョン | fa6adf9ae8fec2a9862187c7a5a39c197737b0af (tree) |
---|---|
日時 | 2019-03-28 19:34:54 |
作者 | Chih-Wei Huang <cwhuang@linu...> |
コミッター | Chih-Wei Huang |
libsuspend: add powerbtnd thread
Also send wakeup key on resume to ensure the system wakes up normally.
@@ -32,20 +32,28 @@ | ||
32 | 32 | #include <android-base/logging.h> |
33 | 33 | #include <android-base/strings.h> |
34 | 34 | #include <android-base/properties.h> |
35 | +#include <android-base/stringprintf.h> | |
36 | + | |
37 | +#include <linux/uinput.h> | |
38 | +#include <dirent.h> | |
39 | +#include <poll.h> | |
35 | 40 | |
36 | 41 | #include "autosuspend_ops.h" |
37 | 42 | |
38 | 43 | #define BASE_SLEEP_TIME 100000 |
39 | 44 | #define MAX_SLEEP_TIME 60000000 |
45 | +#define MAX_POWERBTNS 3 | |
40 | 46 | |
41 | 47 | static constexpr char default_sleep_state[] = "mem"; |
42 | 48 | static constexpr char fallback_sleep_state[] = "freeze"; |
43 | 49 | |
44 | 50 | static int state_fd = -1; |
51 | +static int uinput_fd = -1; | |
45 | 52 | static int wakeup_count_fd; |
46 | 53 | |
47 | 54 | using android::base::GetProperty; |
48 | 55 | using android::base::ReadFdToString; |
56 | +using android::base::StringPrintf; | |
49 | 57 | using android::base::Trim; |
50 | 58 | using android::base::WriteStringToFd; |
51 | 59 |
@@ -66,6 +74,151 @@ static void update_sleep_time(bool success) { | ||
66 | 74 | sleep_time = MIN(sleep_time * 2, MAX_SLEEP_TIME); |
67 | 75 | } |
68 | 76 | |
77 | +static void emit_key(int ufd, int key_code, int val) | |
78 | +{ | |
79 | + struct input_event iev; | |
80 | + iev.type = EV_KEY; | |
81 | + iev.code = key_code; | |
82 | + iev.value = val; | |
83 | + iev.time.tv_sec = 0; | |
84 | + iev.time.tv_usec = 0; | |
85 | + write(ufd, &iev, sizeof(iev)); | |
86 | + iev.type = EV_SYN; | |
87 | + iev.code = SYN_REPORT; | |
88 | + iev.value = 0; | |
89 | + write(ufd, &iev, sizeof(iev)); | |
90 | + LOG(INFO) << StringPrintf("send key %d (%d) on fd %d", key_code, val, ufd); | |
91 | +} | |
92 | + | |
93 | +static void send_key_wakeup(int ufd) | |
94 | +{ | |
95 | + emit_key(ufd, KEY_WAKEUP, 1); | |
96 | + emit_key(ufd, KEY_WAKEUP, 0); | |
97 | +} | |
98 | + | |
99 | +static void send_key_power(int ufd, bool longpress) | |
100 | +{ | |
101 | + emit_key(ufd, KEY_POWER, 1); | |
102 | + if (longpress) sleep(2); | |
103 | + emit_key(ufd, KEY_POWER, 0); | |
104 | +} | |
105 | + | |
106 | +static int openfds(struct pollfd pfds[]) | |
107 | +{ | |
108 | + int cnt = 0; | |
109 | + const char *dirname = "/dev/input"; | |
110 | + struct dirent *de; | |
111 | + DIR *dir; | |
112 | + | |
113 | + if ((dir = opendir(dirname))) { | |
114 | + while ((cnt < MAX_POWERBTNS) && (de = readdir(dir))) { | |
115 | + int fd; | |
116 | + char name[PATH_MAX]; | |
117 | + if (de->d_name[0] != 'e') /* eventX */ | |
118 | + continue; | |
119 | + snprintf(name, PATH_MAX, "%s/%s", dirname, de->d_name); | |
120 | + fd = open(name, O_RDWR | O_NONBLOCK); | |
121 | + if (fd < 0) { | |
122 | + LOG(ERROR) << StringPrintf("could not open %s, %s", name, strerror(errno)); | |
123 | + continue; | |
124 | + } | |
125 | + name[sizeof(name) - 1] = '\0'; | |
126 | + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { | |
127 | + LOG(ERROR) << StringPrintf("could not get device name for %s, %s", name, strerror(errno)); | |
128 | + name[0] = '\0'; | |
129 | + } | |
130 | + // TODO: parse /etc/excluded-input-devices.xml | |
131 | + if (strcmp(name, "Power Button")) { | |
132 | + close(fd); | |
133 | + continue; | |
134 | + } | |
135 | + | |
136 | + LOG(INFO) << StringPrintf("open %s(%s) ok fd=%d", de->d_name, name, fd); | |
137 | + pfds[cnt].events = POLLIN; | |
138 | + pfds[cnt++].fd = fd; | |
139 | + } | |
140 | + closedir(dir); | |
141 | + } | |
142 | + | |
143 | + return cnt; | |
144 | +} | |
145 | + | |
146 | +static void *powerbtnd_thread_func(void *arg __attribute__((unused))) | |
147 | +{ | |
148 | + int cnt, timeout, pollres; | |
149 | + bool longpress = true; | |
150 | + bool doubleclick = android::base::GetBoolProperty("poweroff.doubleclick", false); | |
151 | + struct pollfd pfds[MAX_POWERBTNS]; | |
152 | + | |
153 | + timeout = -1; | |
154 | + cnt = openfds(pfds); | |
155 | + | |
156 | + while (cnt > 0) { | |
157 | + if ((pollres = poll(pfds, cnt, timeout)) < 0) { | |
158 | + LOG(ERROR) << "poll error: " << strerror(errno); | |
159 | + break; | |
160 | + } | |
161 | + LOG(VERBOSE) << "pollres=" << pollres << " timeout=" << timeout; | |
162 | + if (pollres == 0) { | |
163 | + LOG(INFO) << "timeout, send one power key"; | |
164 | + send_key_power(uinput_fd, 0); | |
165 | + timeout = -1; | |
166 | + longpress = true; | |
167 | + continue; | |
168 | + } | |
169 | + for (int i = 0; i < cnt; ++i) { | |
170 | + if (pfds[i].revents & POLLIN) { | |
171 | + struct input_event iev; | |
172 | + size_t res = read(pfds[i].fd, &iev, sizeof(iev)); | |
173 | + if (res < sizeof(iev)) { | |
174 | + LOG(WARNING) << StringPrintf("insufficient input data(%zd)? fd=%d", res, pfds[i].fd); | |
175 | + continue; | |
176 | + } | |
177 | + LOG(DEBUG) << StringPrintf("type=%d code=%d value=%d from fd=%d", iev.type, iev.code, iev.value, pfds[i].fd); | |
178 | + if (iev.type == EV_KEY && iev.code == KEY_POWER && !iev.value) { | |
179 | + if (!doubleclick || timeout > 0) { | |
180 | + send_key_power(uinput_fd, longpress); | |
181 | + timeout = -1; | |
182 | + } else { | |
183 | + timeout = 1000; // one second | |
184 | + } | |
185 | + } else if (iev.type == EV_SYN && iev.code == SYN_REPORT && iev.value) { | |
186 | + LOG(INFO) << "got a resuming event"; | |
187 | + longpress = false; | |
188 | + timeout = 1000; // one second | |
189 | + } | |
190 | + } | |
191 | + } | |
192 | + } | |
193 | + | |
194 | + return NULL; | |
195 | +} | |
196 | + | |
197 | +static void init_android_power_button() | |
198 | +{ | |
199 | + static pthread_t powerbtnd_thread; | |
200 | + struct uinput_user_dev ud; | |
201 | + | |
202 | + if (uinput_fd >= 0) return; | |
203 | + | |
204 | + uinput_fd = open("/dev/uinput", O_WRONLY | O_NDELAY); | |
205 | + if (uinput_fd < 0) { | |
206 | + LOG(ERROR) << "could not open uinput device: " << strerror(errno); | |
207 | + return; | |
208 | + } | |
209 | + | |
210 | + memset(&ud, 0, sizeof(ud)); | |
211 | + strcpy(ud.name, "Android Power Button"); | |
212 | + write(uinput_fd, &ud, sizeof(ud)); | |
213 | + ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY); | |
214 | + ioctl(uinput_fd, UI_SET_KEYBIT, KEY_POWER); | |
215 | + ioctl(uinput_fd, UI_SET_KEYBIT, KEY_WAKEUP); | |
216 | + ioctl(uinput_fd, UI_DEV_CREATE, 0); | |
217 | + | |
218 | + pthread_create(&powerbtnd_thread, NULL, powerbtnd_thread_func, NULL); | |
219 | + pthread_setname_np(powerbtnd_thread, "powerbtnd"); | |
220 | +} | |
221 | + | |
69 | 222 | static bool sleep_state_available(const char *state) |
70 | 223 | { |
71 | 224 | std::string buf; |
@@ -128,6 +281,9 @@ static void* suspend_thread_func(void* arg __attribute__((unused))) { | ||
128 | 281 | if (WriteStringToFd(wakeup_count, wakeup_count_fd)) { |
129 | 282 | LOG(VERBOSE) << "write " << sleep_state << " to " << sys_power_state; |
130 | 283 | success = WriteStringToFd(sleep_state, state_fd); |
284 | + if (success) { | |
285 | + send_key_wakeup(uinput_fd); | |
286 | + } | |
131 | 287 | |
132 | 288 | void (*func)(bool success) = wakeup_func; |
133 | 289 | if (func != NULL) { |
@@ -266,5 +422,6 @@ struct autosuspend_ops autosuspend_wakeup_count_ops = { | ||
266 | 422 | }; |
267 | 423 | |
268 | 424 | struct autosuspend_ops* autosuspend_wakeup_count_init(void) { |
425 | + init_android_power_button(); | |
269 | 426 | return &autosuspend_wakeup_count_ops; |
270 | 427 | } |