hardware/libhardware_legacy
Revisión | 1d378ad170a6a267246b29c0dc10d5fd75c77912 (tree) |
---|---|
Tiempo | 2013-07-25 17:43:53 |
Autor | Chih-Wei Huang <cw_huang@htc....> |
Commiter | Chih-Wei Huang |
add powerbtnd to simulate long press of power button.
On x86 PC, power button usually generates key press and release events
simultaneously. However, the android framework expects a long press
of power button to invoke the power off dialog. The daemon simulates a
long press of power button,
On poweroff.doubleclick=0, one click to power button invokes poweroff
dialog directly.
On poweroff.doubleclick=1, one click to power button suspends the system,
while double click in one second invokes poweroff dialog.
A target needs to add excluded-input-devices.xml to exclude
'Power Button' from processing by EventHub.
@@ -44,6 +44,17 @@ LOCAL_SHARED_LIBRARIES := libcutils | ||
44 | 44 | |
45 | 45 | include $(BUILD_SHARED_LIBRARY) |
46 | 46 | |
47 | +# powerbtn executable | |
48 | +include $(CLEAR_VARS) | |
49 | + | |
50 | +LOCAL_SRC_FILES := power/powerbtnd.c | |
51 | + | |
52 | +LOCAL_MODULE := powerbtnd | |
53 | +LOCAL_MODULE_TAGS := optional | |
54 | +LOCAL_SHARED_LIBRARIES := liblog libcutils | |
55 | + | |
56 | +include $(BUILD_EXECUTABLE) | |
57 | + | |
47 | 58 | # legacy_audio builds it's own set of libraries that aren't linked into |
48 | 59 | # hardware_legacy |
49 | 60 | include $(LEGACY_AUDIO_MAKEFILES) |
@@ -0,0 +1,147 @@ | ||
1 | +/** | |
2 | + * A daemon to simulate power button of Android | |
3 | + * | |
4 | + * Copyright (C) 2011-2012 The Android-x86 Open Source Project | |
5 | + * | |
6 | + * by Chih-Wei Huang <cwhuang@linux.org.tw> | |
7 | + * | |
8 | + * Licensed under GPLv2 or later | |
9 | + * | |
10 | + **/ | |
11 | + | |
12 | +#define LOG_TAG "powerbtn" | |
13 | + | |
14 | +#include <sys/stat.h> | |
15 | +#include <poll.h> | |
16 | +#include <fcntl.h> | |
17 | +#include <errno.h> | |
18 | +#include <dirent.h> | |
19 | +#include <cutils/log.h> | |
20 | +#include <linux/input.h> | |
21 | +#include <linux/uinput.h> | |
22 | +#include <cutils/properties.h> | |
23 | + | |
24 | +const int MAX_POWERBTNS = 3; | |
25 | + | |
26 | +int openfds(struct pollfd pfds[]) | |
27 | +{ | |
28 | + int cnt = 0; | |
29 | + const char *dirname = "/dev/input"; | |
30 | + DIR *dir; | |
31 | + if ((dir = opendir(dirname))) { | |
32 | + int fd; | |
33 | + struct dirent *de; | |
34 | + while ((de = readdir(dir))) { | |
35 | + if (de->d_name[0] != 'e') // eventX | |
36 | + continue; | |
37 | + char name[PATH_MAX]; | |
38 | + snprintf(name, PATH_MAX, "%s/%s", dirname, de->d_name); | |
39 | + fd = open(name, O_RDWR | O_NONBLOCK); | |
40 | + if (fd < 0) { | |
41 | + ALOGE("could not open %s, %s", name, strerror(errno)); | |
42 | + continue; | |
43 | + } | |
44 | + name[sizeof(name) - 1] = '\0'; | |
45 | + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { | |
46 | + ALOGE("could not get device name for %s, %s\n", name, strerror(errno)); | |
47 | + name[0] = '\0'; | |
48 | + } | |
49 | + | |
50 | + // TODO: parse /etc/excluded-input-devices.xml | |
51 | + if (!strcmp(name, "Power Button")) { | |
52 | + ALOGI("open %s(%s) ok", de->d_name, name); | |
53 | + pfds[cnt].events = POLLIN; | |
54 | + pfds[cnt++].fd = fd; | |
55 | + if (cnt < MAX_POWERBTNS) | |
56 | + continue; | |
57 | + else | |
58 | + break; | |
59 | + } | |
60 | + close(fd); | |
61 | + } | |
62 | + closedir(dir); | |
63 | + } | |
64 | + | |
65 | + return cnt; | |
66 | +} | |
67 | + | |
68 | +void send_power(int ufd, int down) | |
69 | +{ | |
70 | + struct input_event iev; | |
71 | + iev.type = EV_KEY; | |
72 | + iev.code = KEY_POWER; | |
73 | + iev.value = down; | |
74 | + write(ufd, &iev, sizeof(iev)); | |
75 | + iev.type = EV_SYN; | |
76 | + iev.code = SYN_REPORT; | |
77 | + iev.value = 0; | |
78 | + write(ufd, &iev, sizeof(iev)); | |
79 | +} | |
80 | + | |
81 | +void simulate_powerkey(int ufd, int longpress) | |
82 | +{ | |
83 | + send_power(ufd, 1); | |
84 | + if (longpress) | |
85 | + sleep(2); | |
86 | + send_power(ufd, 0); | |
87 | +} | |
88 | + | |
89 | +int main() | |
90 | +{ | |
91 | + struct pollfd pfds[MAX_POWERBTNS]; | |
92 | + int cnt = openfds(pfds); | |
93 | + int timeout = -1; | |
94 | + char prop[PROPERTY_VALUE_MAX]; | |
95 | + | |
96 | + int ufd = open("/dev/uinput", O_WRONLY | O_NDELAY); | |
97 | + if (ufd >= 0) { | |
98 | + struct uinput_user_dev ud; | |
99 | + memset(&ud, 0, sizeof(ud)); | |
100 | + strcpy(ud.name, "Android Power Button"); | |
101 | + write(ufd, &ud, sizeof(ud)); | |
102 | + ioctl(ufd, UI_SET_EVBIT, EV_KEY); | |
103 | + ioctl(ufd, UI_SET_KEYBIT, KEY_POWER); | |
104 | + ioctl(ufd, UI_DEV_CREATE, 0); | |
105 | + } else { | |
106 | + ALOGE("could not open uinput device: %s", strerror(errno)); | |
107 | + return -1; | |
108 | + } | |
109 | + | |
110 | + property_get("poweroff.doubleclick", prop, NULL); | |
111 | + | |
112 | + for (;;) { | |
113 | + int i; | |
114 | + int pollres = poll(pfds, cnt, timeout) ; | |
115 | + ALOGV("pollres=%d %d\n", pollres, timeout); | |
116 | + if (pollres < 0) { | |
117 | + ALOGE("poll error: %s", strerror(errno)); | |
118 | + break; | |
119 | + } | |
120 | + if (pollres == 0) { // timeout, send one power key | |
121 | + simulate_powerkey(ufd, 0); | |
122 | + timeout = -1; | |
123 | + continue; | |
124 | + } | |
125 | + for (i = 0; i < cnt; ++i) { | |
126 | + if (pfds[i].revents & POLLIN) { | |
127 | + struct input_event iev; | |
128 | + size_t res = read(pfds[i].fd, &iev, sizeof(iev)); | |
129 | + if (res < sizeof(iev)) { | |
130 | + ALOGW("insufficient input data(%d)? fd=%d", res, pfds[i].fd); | |
131 | + continue; | |
132 | + } | |
133 | + ALOGV("type=%d scancode=%d value=%d from fd=%d", iev.type, iev.code, iev.value, pfds[i].fd); | |
134 | + if (iev.type == EV_KEY && iev.code == KEY_POWER && !iev.value) { | |
135 | + if (prop[0] != '1' || timeout > 0) { | |
136 | + simulate_powerkey(ufd, 1); | |
137 | + timeout = -1; | |
138 | + } else { | |
139 | + timeout = 1000; // one second | |
140 | + } | |
141 | + } | |
142 | + } | |
143 | + } | |
144 | + } | |
145 | + | |
146 | + return 0; | |
147 | +} |