OSDN Git Service

add powerbtnd to simulate long press of power button.
authorChih-Wei Huang <cwhuang@linux.org.tw>
Tue, 12 Jul 2011 06:58:56 +0000 (14:58 +0800)
committerChih-Wei Huang <cwhuang@linux.org.tw>
Mon, 19 Dec 2011 02:59:15 +0000 (10:59 +0800)
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 in 2 seconds.

A target needs to add excluded-input-devices.xml to exclude
'Power Button' from processing by EventHub.

Android.mk
power/powerbtnd.c [new file with mode: 0644]

index 1faf6e2..47083b9 100644 (file)
@@ -44,6 +44,17 @@ LOCAL_SHARED_LIBRARIES := libcutils
 
 include $(BUILD_SHARED_LIBRARY)
 
+# powerbtn executable
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := power/powerbtnd.c
+
+LOCAL_MODULE := powerbtnd
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES := liblog
+
+include $(BUILD_EXECUTABLE)
+
 # legacy_audio builds it's own set of libraries that aren't linked into
 # hardware_legacy
 include $(LEGACY_AUDIO_MAKEFILES)
diff --git a/power/powerbtnd.c b/power/powerbtnd.c
new file mode 100644 (file)
index 0000000..26494ab
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * A daemon to simulate power button of Android
+ *
+ * Copyright (C) 2011 The Android-x86 Open Source Project
+ *
+ * by Chih-Wei Huang <cwhuang@linux.org.tw>
+ *
+ * Licensed under GPLv2 or later
+ *
+ **/
+
+#define LOG_TAG "powerbtn"
+
+#include <sys/stat.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dirent.h>
+#include <cutils/log.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+
+const int MAX_POWERBTNS = 3;
+
+int openfds(struct pollfd pfds[])
+{
+       int cnt = 0;
+       const char *dirname = "/dev/input";
+       DIR *dir;
+       if ((dir = opendir(dirname))) {
+               int fd;
+               struct dirent *de;
+               while ((de = readdir(dir))) {
+                       if (de->d_name[0] != 'e') // eventX
+                               continue;
+                       char name[PATH_MAX];
+                       snprintf(name, PATH_MAX, "%s/%s", dirname, de->d_name);
+                       fd = open(name, O_RDWR);
+                       if (fd < 0) {
+                               LOGE("could not open %s, %s", name, strerror(errno));
+                               continue;
+                       }
+                       name[sizeof(name) - 1] = '\0';
+                       if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
+                               LOGE("could not get device name for %s, %s\n", name, strerror(errno));
+                               name[0] = '\0';
+                       }
+
+                       // TODO: parse /etc/excluded-input-devices.xml
+                       if (!strcmp(name, "Power Button")) {
+                               LOGI("open %s(%s) ok", de->d_name, name);
+                               pfds[cnt].events = POLLIN;
+                               pfds[cnt++].fd = fd;
+                               if (cnt < MAX_POWERBTNS)
+                                       continue;
+                               else
+                                       break;
+                       }
+                       close(fd);
+               }
+               closedir(dir);
+       }
+
+       return cnt;
+}
+
+int main()
+{
+       struct pollfd pfds[MAX_POWERBTNS];
+       int cnt = openfds(pfds);
+       int pollres;
+
+       int ufd = open("/dev/uinput", O_WRONLY | O_NDELAY);
+       if (ufd >= 0) {
+               struct uinput_user_dev ud;
+               memset(&ud, 0, sizeof(ud));
+               strcpy(ud.name, "Android Power Button");
+               write(ufd, &ud, sizeof(ud));
+               ioctl(ufd, UI_SET_EVBIT, EV_KEY);
+               ioctl(ufd, UI_SET_KEYBIT, KEY_POWER);
+               ioctl(ufd, UI_DEV_CREATE, 0);
+       } else {
+               LOGE("could not open uinput device: %s", strerror(errno));
+               return -1;
+       }
+
+       while ((pollres = poll(pfds, cnt, -1))) {
+               int i;
+               if (pollres < 0) {
+                       LOGE("poll error: %s", strerror(errno));
+                       break;
+               }
+               for (i = 0; i < cnt; ++i) {
+                       if (pfds[i].revents & POLLIN) {
+                               struct input_event iev;
+                               size_t res = read(pfds[i].fd, &iev, sizeof(iev));
+                               if (res < sizeof(iev)) {
+                                       LOGW("insufficient input data(%d)? fd=%d", res, pfds[i].fd);
+                                       continue;
+                               }
+                               LOGV("type=%d scancode=%d value=%d from fd=%d", iev.type, iev.code, iev.value, pfds[i].fd);
+                               if (iev.type == EV_KEY) {
+                                       switch (iev.code)
+                                       {
+                                               case KEY_POWER:
+                                                       if (!iev.value)
+                                                               sleep(2);
+                                                       break;
+                                       }
+                               }
+
+                               write(ufd, &iev, sizeof(iev));
+                       }
+               }
+       }
+
+       return 0;
+}