auto import from //branches/cupcake/...@125939
diff --git a/init/devices.c b/init/devices.c
index f86eab9..44e263d 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -122,6 +122,7 @@
     { "/dev/msm_pcm_out",   0660,   AID_SYSTEM,     AID_AUDIO,      1 },
     { "/dev/msm_pcm_in",    0660,   AID_SYSTEM,     AID_AUDIO,      1 },
     { "/dev/msm_pcm_ctl",   0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/msm_snd",       0660,   AID_SYSTEM,     AID_AUDIO,      1 },
     { "/dev/msm_mp3",       0660,   AID_SYSTEM,     AID_AUDIO,      1 },
     { "/dev/smd0",          0640,   AID_RADIO,      AID_RADIO,      0 },
     { "/dev/qmi",           0640,   AID_RADIO,      AID_RADIO,      0 },
diff --git a/init/init.c b/init/init.c
index 3616840..b8b4f40 100644
--- a/init/init.c
+++ b/init/init.c
@@ -37,6 +37,7 @@
 #include <cutils/sockets.h>
 #include <termios.h>
 #include <linux/kd.h>
+#include <linux/keychord.h>
 
 #include <sys/system_properties.h>
 
@@ -60,6 +61,9 @@
 static char hardware[32];
 static unsigned revision = 0;
 static char qemu[32];
+static struct input_keychord *keychords = 0;
+static int keychords_count = 0;
+static int keychords_length = 0;
 
 static void drain_action_queue(void);
 
@@ -233,7 +237,7 @@
 
         setpgid(0, getpid());
 
-	/* as requested, set our gid, supplemental gids, and uid */
+    /* as requested, set our gid, supplemental gids, and uid */
         if (svc->gid) {
             setgid(svc->gid);
         }
@@ -658,17 +662,101 @@
     exit(1);
 }
 
+void add_service_keycodes(struct service *svc)
+{
+    struct input_keychord *keychord;
+    int i, size;
+
+    if (svc->keycodes) {
+        /* add a new keychord to the list */
+        size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]);
+        keychords = realloc(keychords, keychords_length + size);
+        if (!keychords) {
+            ERROR("could not allocate keychords\n");
+            keychords_length = 0;
+            keychords_count = 0;
+            return;
+        }
+
+        keychord = (struct input_keychord *)((char *)keychords + keychords_length);
+        keychord->version = KEYCHORD_VERSION;
+        keychord->id = keychords_count + 1;
+        keychord->count = svc->nkeycodes;
+        svc->keychord_id = keychord->id;
+
+        for (i = 0; i < svc->nkeycodes; i++) {
+            keychord->keycodes[i] = svc->keycodes[i];
+        }
+        keychords_count++;
+        keychords_length += size;
+    }
+}
+
+int open_keychord()
+{
+    int fd, ret;
+
+    service_for_each(add_service_keycodes);
+    
+    /* nothing to do if no services require keychords */
+    if (!keychords)
+        return -1;
+
+    fd = open("/dev/keychord", O_RDWR);
+    if (fd < 0) {
+        ERROR("could not open /dev/keychord\n");
+        return fd;
+    }
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+    ret = write(fd, keychords, keychords_length);
+    if (ret != keychords_length) {
+        ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno);
+        close(fd);
+        fd = -1;
+    }
+
+    free(keychords);
+    keychords = 0;
+
+    return fd;
+}
+
+void handle_keychord(int fd)
+{
+    struct service *svc;
+    int ret;
+    __u16 id;
+
+    ret = read(fd, &id, sizeof(id));
+    if (ret != sizeof(id)) {
+        ERROR("could not read keychord id\n");
+        return;
+    }
+
+    svc = service_find_by_keychord(id);
+    if (svc) {
+        INFO("starting service %s from keychord\n", svc->name);
+        service_start(svc);   
+    } else {
+        ERROR("service for keychord %d not found\n", id);
+    }
+}
+
 int main(int argc, char **argv)
 {
     int device_fd = -1;
     int property_set_fd = -1;
     int signal_recv_fd = -1;
+    int keychord_fd = -1;
+    int fd_count;
     int s[2];
     int fd;
     struct sigaction act;
     char tmp[PROP_VALUE_MAX];
     struct pollfd ufds[4];
     char *tmpdev;
+    char* debuggable;
 
     act.sa_handler = sigchld_handler;
     act.sa_flags = SA_NOCLDSTOP;
@@ -721,6 +809,12 @@
     device_fd = device_init();
 
     property_init();
+    
+    // only listen for keychords if ro.debuggable is true
+    debuggable = property_get("ro.debuggable");
+    if (debuggable && !strcmp(debuggable, "1")) {
+        keychord_fd = open_keychord();
+    }
 
     if (console[0]) {
         snprintf(tmp, sizeof(tmp), "/dev/%s", console);
@@ -733,27 +827,27 @@
     close(fd);
 
     if( load_565rle_image(INIT_IMAGE_FILE) ) {
-	fd = open("/dev/tty0", O_WRONLY);
-	if (fd >= 0) {
-	    const char *msg;
+    fd = open("/dev/tty0", O_WRONLY);
+    if (fd >= 0) {
+        const char *msg;
             msg = "\n"
-		"\n"
-		"\n"
-		"\n"
-		"\n"
-		"\n"
-		"\n"  // console is 40 cols x 30 lines
-		"\n"
-		"\n"
-		"\n"
-		"\n"
-		"\n"
-		"\n"
-		"\n"
-		"             A N D R O I D ";
-	    write(fd, msg, strlen(msg));
-	    close(fd);
-	}
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"  // console is 40 cols x 30 lines
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "             A N D R O I D ";
+        write(fd, msg, strlen(msg));
+        close(fd);
+    }
     }
 
     if (qemu[0])
@@ -823,6 +917,16 @@
     ufds[1].events = POLLIN;
     ufds[2].fd = signal_recv_fd;
     ufds[2].events = POLLIN;
+    fd_count = 3;
+
+    if (keychord_fd > 0) {
+        ufds[3].fd = keychord_fd;
+        ufds[3].events = POLLIN;
+        fd_count++;
+    } else {
+        ufds[3].events = 0;
+        ufds[3].revents = 0;
+    }
 
 #if BOOTCHART
     bootchart_count = bootchart_init();
@@ -836,11 +940,10 @@
 #endif
 
     for(;;) {
-        int nr, timeout = -1;
+        int nr, i, timeout = -1;
 
-        ufds[0].revents = 0;
-        ufds[1].revents = 0;
-        ufds[2].revents = 0;
+        for (i = 0; i < fd_count; i++)
+            ufds[i].revents = 0;
 
         drain_action_queue();
         restart_processes();
@@ -861,7 +964,7 @@
             }
         }
 #endif
-        nr = poll(ufds, 3, timeout);
+        nr = poll(ufds, fd_count, timeout);
         if (nr <= 0)
             continue;
 
@@ -878,6 +981,8 @@
 
         if (ufds[1].revents == POLLIN)
             handle_property_set_fd(property_set_fd);
+        if (ufds[3].revents == POLLIN)
+            handle_keychord(keychord_fd);
     }
 
     return 0;
diff --git a/init/init.h b/init/init.h
index 4ff0c69..b686869 100644
--- a/init/init.h
+++ b/init/init.h
@@ -139,12 +139,19 @@
     int nargs;
     char *args[1];
     struct action onrestart;  /* Actions to execute on restart. */
+    
+    /* keycodes for triggering this service via /dev/keychord */
+    int *keycodes;
+    int nkeycodes;
+    int keychord_id;
 };
 
 int parse_config_file(const char *fn);
 
 struct service *service_find_by_name(const char *name);
 struct service *service_find_by_pid(pid_t pid);
+struct service *service_find_by_keychord(int keychord_id);
+void service_for_each(void (*func)(struct service *svc));
 void service_for_each_class(const char *classname,
                             void (*func)(struct service *svc));
 void service_for_each_flags(unsigned matchflags,
diff --git a/init/keywords.h b/init/keywords.h
index 058996e..6f47379 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -45,6 +45,7 @@
     KEYWORD(ifup,        COMMAND, 1, do_ifup)
     KEYWORD(insmod,      COMMAND, 1, do_insmod)
     KEYWORD(import,      COMMAND, 1, do_import)
+    KEYWORD(keycodes,    OPTION,  0, 0)
     KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
     KEYWORD(mount,       COMMAND, 3, do_mount)
     KEYWORD(on,          SECTION, 0, 0)
diff --git a/init/parser.c b/init/parser.c
index a51691b..6a22d24 100644
--- a/init/parser.c
+++ b/init/parser.c
@@ -158,6 +158,9 @@
         if (!strcmp(s, "nsmod")) return K_insmod;
         if (!strcmp(s, "mport")) return K_import;
         break;
+    case 'k':
+        if (!strcmp(s, "eycodes")) return K_keycodes;
+        break;
     case 'l':
         if (!strcmp(s, "oglevel")) return K_loglevel;
         break;
@@ -183,7 +186,7 @@
         if (!strcmp(s, "tart")) return K_start;
         if (!strcmp(s, "top")) return K_stop;
         if (!strcmp(s, "ymlink")) return K_symlink;
-	if (!strcmp(s, "ysclktz")) return K_sysclktz;
+        if (!strcmp(s, "ysclktz")) return K_sysclktz;
         break;
     case 't':
         if (!strcmp(s, "rigger")) return K_trigger;
@@ -440,6 +443,29 @@
     return 0;
 }
 
+struct service *service_find_by_keychord(int keychord_id)
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (svc->keychord_id == keychord_id) {
+            return svc;
+        }
+    }
+    return 0;
+}
+
+void service_for_each(void (*func)(struct service *svc))
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        func(svc);
+    }
+}
+
 void service_for_each_class(const char *classname,
                             void (*func)(struct service *svc))
 {
@@ -586,7 +612,7 @@
 {
     struct service *svc = state->context;
     struct command *cmd;
-    int kw, kw_nargs;
+    int i, kw, kw_nargs;
 
     if (nargs == 0) {
         return;
@@ -624,6 +650,21 @@
             svc->nr_supp_gids = n - 2;
         }
         break;
+    case K_keycodes:
+        if (nargs < 2) {
+            parse_error(state, "keycodes option requires atleast one keycode\n");
+        } else {
+            svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
+            if (!svc->keycodes) {
+                parse_error(state, "could not allocate keycodes\n");
+            } else {
+                svc->nkeycodes = nargs - 1;
+                for (i = 1; i < nargs; i++) {
+                    svc->keycodes[i - 1] = atoi(args[i]);
+                }
+            }
+        }
+        break;
     case K_oneshot:
         svc->flags |= SVC_ONESHOT;
         break;