Merge "adb: do not mix printf() with write() when writing to stdout."
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
index 866e3af..fd34052 100644
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -61,6 +61,8 @@
 #define VENDOR_ID_GARMIN_ASUS   0x091E
 // Sharp's USB Vendor ID
 #define VENDOR_ID_SHARP         0x04dd
+// ZTE's USB Vendor ID
+#define VENDOR_ID_ZTE           0x19D2
 
 /** built-in vendor list */
 int builtInVendorIds[] = {
@@ -77,6 +79,7 @@
     VENDOR_ID_NVIDIA,
     VENDOR_ID_GARMIN_ASUS,
     VENDOR_ID_SHARP,
+    VENDOR_ID_ZTE,
 };
 
 #define BUILT_IN_VENDOR_COUNT    (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
diff --git a/include/cutils/iosched_policy.h b/include/cutils/iosched_policy.h
new file mode 100644
index 0000000..07c5d1f
--- /dev/null
+++ b/include/cutils/iosched_policy.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_IOSCHED_POLICY_H
+#define __CUTILS_IOSCHED_POLICY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    IoSchedClass_NONE,
+    IoSchedClass_RT,
+    IoSchedClass_BE,
+    IoSchedClass_IDLE,
+} IoSchedClass;
+
+extern int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio);
+extern int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_IOSCHED_POLICY_H */ 
diff --git a/init/init.c b/init/init.c
index 16a3530..4d98cc2 100755
--- a/init/init.c
+++ b/init/init.c
@@ -35,6 +35,7 @@
 #include <sys/reboot.h>
 
 #include <cutils/sockets.h>
+#include <cutils/iosched_policy.h>
 #include <termios.h>
 #include <linux/kd.h>
 #include <linux/keychord.h>
@@ -224,6 +225,13 @@
             }
         }
 
+        if (svc->ioprio_class != IoSchedClass_NONE) {
+            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
+                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
+                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
+            }
+        }
+
         if (needs_console) {
             setsid();
             open_console();
diff --git a/init/init.h b/init/init.h
index 60c3055..8c0571c 100644
--- a/init/init.h
+++ b/init/init.h
@@ -146,6 +146,9 @@
     int nkeycodes;
     int keychord_id;
 
+    int ioprio_class;
+    int ioprio_pri;
+
     int nargs;
     /* "MUST BE AT THE END OF THE STRUCT" */
     char *args[1];
diff --git a/init/keywords.h b/init/keywords.h
index 308118e..254c785 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -75,6 +75,7 @@
     KEYWORD(chmod,       COMMAND, 2, do_chmod)
     KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
     KEYWORD(device,      COMMAND, 4, do_device)
+    KEYWORD(ioprio,      OPTION,  0, 0)
 #ifdef __MAKE_KEYWORD_ENUM__
     KEYWORD_COUNT,
 };
diff --git a/init/parser.c b/init/parser.c
index 54622cc..7da0d19 100644
--- a/init/parser.c
+++ b/init/parser.c
@@ -10,6 +10,8 @@
 #include "init.h"
 #include "property_service.h"
 
+#include <cutils/iosched_policy.h>
+
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
@@ -155,6 +157,7 @@
         if (!strcmp(s, "ostname")) return K_hostname;
         break;
     case 'i':
+        if (!strcmp(s, "oprio")) return K_ioprio;
         if (!strcmp(s, "fup")) return K_ifup;
         if (!strcmp(s, "nsmod")) return K_insmod;
         if (!strcmp(s, "mport")) return K_import;
@@ -619,6 +622,8 @@
         return;
     }
     
+    svc->ioprio_class = IoSchedClass_NONE;
+
     kw = lookup_keyword(args[0]);
     switch (kw) {
     case K_capability:
@@ -636,6 +641,28 @@
     case K_disabled:
         svc->flags |= SVC_DISABLED;
         break;
+    case K_ioprio:
+        if (nargs != 3) {
+            parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
+        } else {
+            svc->ioprio_pri = strtoul(args[2], 0, 8);
+
+            if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
+                parse_error(state, "priority value must be range 0 - 7\n");
+                break;
+            }
+
+            if (!strcmp(args[1], "rt")) {
+                svc->ioprio_class = IoSchedClass_RT;
+            } else if (!strcmp(args[1], "be")) {
+                svc->ioprio_class = IoSchedClass_BE;
+            } else if (!strcmp(args[1], "idle")) {
+                svc->ioprio_class = IoSchedClass_IDLE;
+            } else {
+                parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
+            }
+        }
+        break;
     case K_group:
         if (nargs < 2) {
             parse_error(state, "group option requires a group id\n");
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 93933e2..4c45cc9 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -38,7 +38,8 @@
 	process_name.c \
 	properties.c \
 	threads.c \
-	sched_policy.c
+	sched_policy.c \
+	iosched_policy.c
 
 commonHostSources := \
         ashmem-host.c
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c
new file mode 100644
index 0000000..f350f58
--- /dev/null
+++ b/libcutils/iosched_policy.c
@@ -0,0 +1,67 @@
+
+/* libs/cutils/iosched_policy.c
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SCHED_H
+
+#include <cutils/iosched_policy.h>
+
+extern int ioprio_set(int which, int who, int ioprio);
+
+enum {
+    WHO_PROCESS = 1,
+    WHO_PGRP,
+    WHO_USER,
+};
+
+#define CLASS_SHIFT 13
+#define IOPRIO_NORM 4
+
+int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio) {
+#ifdef HAVE_ANDROID_OS
+    if (ioprio_set(WHO_PROCESS, pid, ioprio | (clazz << CLASS_SHIFT))) {
+        return -1;
+    }
+#endif
+    return 0;
+}
+
+int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio) {
+#ifdef HAVE_ANDROID_OS
+    int rc;
+
+    if ((rc = ioprio_get(WHO_PROCESS, pid)) < 0) {
+        return -1;
+    }
+
+    *clazz = (rc >> CLASS_SHIFT);
+    *ioprio = (rc & 0xff);
+#else
+    *clazz = IoSchedClass_NONE;
+    *ioprio = 0;
+#endif
+    return 0;
+}
+
+#endif /* HAVE_SCHED_H */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 3130a1c..43c9ccd 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -30,6 +30,57 @@
 
 #define LOG_FILE_DIR    "/dev/log/"
 
+struct queued_entry_t {
+    union {
+        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
+        struct logger_entry entry __attribute__((aligned(4)));
+    };
+    queued_entry_t* next;
+
+    queued_entry_t() {
+        next = NULL;
+    }
+};
+
+static int cmp(queued_entry_t* a, queued_entry_t* b) {
+    int n = a->entry.sec - b->entry.sec;
+    if (n != 0) {
+        return n;
+    }
+    return a->entry.nsec - b->entry.nsec;
+}
+
+struct log_device_t {
+    char* device;
+    bool binary;
+    int fd;
+    bool printed;
+    char label;
+
+    queued_entry_t* queue;
+    log_device_t* next;
+
+    log_device_t(char* d, bool b, char l) {
+        device = d;
+        binary = b;
+        label = l;
+        next = NULL;
+        printed = false;
+    }
+
+    void enqueue(queued_entry_t* entry) {
+        if (this->queue == NULL) {
+            this->queue = entry;
+        } else {
+            queued_entry_t** e = &this->queue;
+            while (*e && cmp(entry, *e) >= 0) {
+                e = &((*e)->next);
+            }
+            entry->next = *e;
+            *e = entry;
+        }
+    }
+};
 
 namespace android {
 
@@ -40,8 +91,8 @@
 static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
 static int g_outFD = -1;
 static off_t g_outByteCount = 0;
-static int g_isBinary = 0;
 static int g_printBinary = 0;
+static int g_devCount = 0;
 
 static EventTagMap* g_eventTagMap = NULL;
 
@@ -103,14 +154,37 @@
     } while (ret < 0 && errno == EINTR);
 }
 
-static void processBuffer(struct logger_entry *buf)
+static void processBuffer(log_device_t* dev, struct logger_entry *buf)
 {
     int bytesWritten;
     int err;
     AndroidLogEntry entry;
     char binaryMsgBuf[1024];
 
-    if (g_isBinary) {
+    if (!dev->printed) {
+        dev->printed = true;
+        if (g_devCount > 1) {
+            snprintf(binaryMsgBuf, sizeof(binaryMsgBuf), "--------- beginning of %s\n",
+                    dev->device);
+            bytesWritten = write(g_outFD, binaryMsgBuf, strlen(binaryMsgBuf));
+            if (bytesWritten < 0) {
+                perror("output error");
+                exit(-1);
+            }
+        }
+    }
+
+    if (g_devCount > 1) {
+        binaryMsgBuf[0] = dev->label;
+        binaryMsgBuf[1] = ' ';
+        bytesWritten = write(g_outFD, binaryMsgBuf, 2);
+        if (bytesWritten < 0) {
+            perror("output error");
+            exit(-1);
+        }
+    }
+
+    if (dev->binary) {
         err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap,
                 binaryMsgBuf, sizeof(binaryMsgBuf));
         //printf(">>> pri=%d len=%d msg='%s'\n",
@@ -142,36 +216,125 @@
     return;
 }
 
-static void readLogLines(int logfd)
+static void chooseFirst(log_device_t* dev, log_device_t** firstdev, queued_entry_t** firstentry) {
+    *firstdev = NULL;
+    *firstentry = NULL;
+    int i=0;
+    for (; dev; dev=dev->next) {
+        i++;
+        if (dev->queue) {
+            if ((*firstentry) == NULL || cmp(dev->queue, *firstentry) < 0) {
+                (*firstentry) = dev->queue;
+                *firstdev = dev;
+            }
+        }
+    }
+}
+
+static void printEntry(log_device_t* dev, queued_entry_t* entry) {
+    if (g_printBinary) {
+        printBinary(&entry->entry);
+    } else {
+        processBuffer(dev, &entry->entry);
+    }
+}
+
+static void eatEntry(log_device_t* dev, queued_entry_t* entry) {
+    if (dev->queue != entry) {
+        perror("assertion failed: entry isn't first in queue");
+        exit(1);
+    }
+    printEntry(dev, entry);
+    dev->queue = entry->next;
+    delete entry;
+}
+
+static void readLogLines(log_device_t* devices)
 {
+    log_device_t* dev;
+    int max = 0;
+    queued_entry_t* entry;
+    queued_entry_t* old;
+    int ret;
+    bool somethingForEveryone;
+    bool sleep = true;
+
+    int result;
+    fd_set readset;
+
+    for (dev=devices; dev; dev = dev->next) {
+        if (dev->fd > max) {
+            max = dev->fd;
+        }
+    }
+
     while (1) {
-        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
-        struct logger_entry *entry = (struct logger_entry *) buf;
-        int ret;
+        do {
+            timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR.
+            FD_ZERO(&readset);
+            for (dev=devices; dev; dev = dev->next) {
+                FD_SET(dev->fd, &readset);
+            }
+            result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout);
+        } while (result == -1 && errno == EINTR);
 
-        ret = read(logfd, entry, LOGGER_ENTRY_MAX_LEN);
-        if (ret < 0) {
-            if (errno == EINTR)
-                continue;
-            if (errno == EAGAIN)
-                break;
-            perror("logcat read");
-            exit(EXIT_FAILURE);
+        if (result >= 0) {
+            for (dev=devices; dev; dev = dev->next) {
+                if (FD_ISSET(dev->fd, &readset)) {
+                    entry = new queued_entry_t;
+                    /* NOTE: driver guarantees we read exactly one full entry */
+                    ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN);
+                    if (ret < 0) {
+                        if (errno == EINTR) {
+                            delete entry;
+                            goto next;
+                        }
+                        if (errno == EAGAIN) {
+                            delete entry;
+                            break;
+                        }
+                        perror("logcat read");
+                        exit(EXIT_FAILURE);
+                    }
+                    else if (!ret) {
+                        fprintf(stderr, "read: Unexpected EOF!\n");
+                        exit(EXIT_FAILURE);
+                    }
+
+                    entry->entry.msg[entry->entry.len] = '\0';
+
+                    dev->enqueue(entry);
+                }
+            }
+
+            if (result == 0) {
+                // we did our short timeout trick and there's nothing new
+                // print all that aren't the last in their list
+                sleep = true;
+                while (true) {
+                    chooseFirst(devices, &dev, &entry);
+                    if (!entry) {
+                        break;
+                    }
+                    eatEntry(dev, entry);
+                }
+            } else {
+                // print all that aren't the last in their list
+                while (true) {
+                    chooseFirst(devices, &dev, &entry);
+                    if (!entry) {
+                        sleep = false;
+                        break;
+                    } else if (entry->next == NULL) {
+                        sleep = false;
+                        break;
+                    }
+                    eatEntry(dev, entry);
+                }
+            }
         }
-        else if (!ret) {
-            fprintf(stderr, "read: Unexpected EOF!\n");
-            exit(EXIT_FAILURE);
-        }
-
-        /* NOTE: driver guarantees we read exactly one full entry */
-
-        entry->msg[entry->len] = '\0';
-
-        if (g_printBinary) {
-            printBinary(entry);
-        } else {
-            (void) processBuffer(entry);
-        }
+next:
+        ;
     }
 }
 
@@ -275,16 +438,17 @@
 
 extern "C" void logprint_run_tests(void);
 
-int main (int argc, char **argv)
+int main(int argc, char **argv)
 {
-    int logfd;
     int err;
     int hasSetLogFormat = 0;
     int clearLog = 0;
     int getLogSize = 0;
     int mode = O_RDONLY;
-    char *log_device = strdup("/dev/"LOGGER_LOG_MAIN);
     const char *forceFilters = NULL;
+    log_device_t* devices = NULL;
+    log_device_t* dev;
+    bool needBinary = false;
 
     g_logformat = android_log_format_new();
 
@@ -326,14 +490,27 @@
                 getLogSize = 1;
             break;
 
-            case 'b':
-                free(log_device);
-                log_device =
-                    (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1);
-                strcpy(log_device, LOG_FILE_DIR);
-                strcat(log_device, optarg);
+            case 'b': {
+                char* buf = (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1);
+                strcpy(buf, LOG_FILE_DIR);
+                strcat(buf, optarg);
 
-                android::g_isBinary = (strcmp(optarg, "events") == 0);
+                bool binary = strcmp(optarg, "events") == 0;
+                if (binary) {
+                    needBinary = true;
+                }
+
+                if (devices) {
+                    dev = devices;
+                    while (dev->next) {
+                        dev = dev->next;
+                    }
+                    dev->next = new log_device_t(buf, binary, optarg[0]);
+                } else {
+                    devices = new log_device_t(buf, binary, optarg[0]);
+                }
+                android::g_devCount++;
+            }
             break;
 
             case 'B':
@@ -460,6 +637,11 @@
         }
     }
 
+    if (!devices) {
+        devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, LOGGER_LOG_MAIN[0]);
+        android::g_devCount = 1;
+    }
+
     if (android::g_logRotateSizeKBytes != 0 
         && android::g_outputFileName == NULL
     ) {
@@ -516,42 +698,50 @@
         }
     }
 
-    logfd = open(log_device, mode);
-    if (logfd < 0) {
-        fprintf(stderr, "Unable to open log device '%s': %s\n",
-            log_device, strerror(errno));
-        exit(EXIT_FAILURE);
-    }
-
-    if (clearLog) {
-        int ret;
-        ret = android::clearLog(logfd);
-        if (ret) {
-            perror("ioctl");
+    dev = devices;
+    while (dev) {
+        dev->fd = open(dev->device, mode);
+        if (dev->fd < 0) {
+            fprintf(stderr, "Unable to open log device '%s': %s\n",
+                dev->device, strerror(errno));
             exit(EXIT_FAILURE);
         }
-        return 0;
+
+        if (clearLog) {
+            int ret;
+            ret = android::clearLog(dev->fd);
+            if (ret) {
+                perror("ioctl");
+                exit(EXIT_FAILURE);
+            }
+            return 0;
+        }
+
+        if (getLogSize) {
+            int size, readable;
+
+            size = android::getLogSize(dev->fd);
+            if (size < 0) {
+                perror("ioctl");
+                exit(EXIT_FAILURE);
+            }
+
+            readable = android::getLogReadableSize(dev->fd);
+            if (readable < 0) {
+                perror("ioctl");
+                exit(EXIT_FAILURE);
+            }
+
+            printf("%s: ring buffer is %dKb (%dKb consumed), "
+                   "max entry is %db, max payload is %db\n", dev->device,
+                   size / 1024, readable / 1024,
+                   (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
+        }
+
+        dev = dev->next;
     }
 
     if (getLogSize) {
-        int size, readable;
-
-        size = android::getLogSize(logfd);
-        if (size < 0) {
-            perror("ioctl");
-            exit(EXIT_FAILURE);
-        }
-
-        readable = android::getLogReadableSize(logfd);
-        if (readable < 0) {
-            perror("ioctl");
-            exit(EXIT_FAILURE);
-        }
-
-        printf("ring buffer is %dKb (%dKb consumed), "
-               "max entry is %db, max payload is %db\n",
-               size / 1024, readable / 1024,
-               (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
         return 0;
     }
 
@@ -559,10 +749,10 @@
     //LOG_EVENT_LONG(11, 0x1122334455667788LL);
     //LOG_EVENT_STRING(0, "whassup, doc?");
 
-    if (android::g_isBinary)
+    if (needBinary)
         android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
 
-    android::readLogLines(logfd);
+    android::readLogLines(devices);
 
     return 0;
 }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 059ac0a..97a3b38 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -58,7 +58,9 @@
 
 # Create cgroup mount points for process groups
     mkdir /dev/cpuctl
-    mount cgroup none /dev/cpuctl cpu
+    mount cgroup none /dev/cpuctl cpu,cpuacct
+    mkdir /dev/cpuctl/uid
+    chmod 0775 /dev/cpuctl/uid
     chown system system /dev/cpuctl
     chown system system /dev/cpuctl/tasks
     chmod 0777 /dev/cpuctl/tasks
@@ -281,6 +283,7 @@
 
 service vold /system/bin/vold
     socket vold stream 0660 root mount
+    ioprio be 2
 
 service netd /system/bin/netd
     socket netd stream 0660 root system
@@ -302,6 +305,7 @@
 service media /system/bin/mediaserver
     user media
     group system audio camera graphics inet net_bt net_bt_admin
+    ioprio rt 4
 
 service bootanim /system/bin/bootanimation
     user graphics
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 122a544..a6114ac 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -51,7 +51,8 @@
 	iftop \
 	id \
 	vmstat \
-	nandread
+	nandread \
+        ionice
 
 LOCAL_SRC_FILES:= \
 	toolbox.c \
diff --git a/toolbox/ionice.c b/toolbox/ionice.c
new file mode 100644
index 0000000..4a182f2
--- /dev/null
+++ b/toolbox/ionice.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <cutils/iosched_policy.h>
+
+static char *classes[] = {"none", "rt", "be", "idle", NULL};
+
+int ionice_main(int argc, char *argv[])
+{
+    IoSchedClass clazz = IoSchedClass_NONE;
+    int ioprio = 0;
+    int pid;
+
+    if(argc != 2 && argc != 4) {
+        fprintf(stderr, "usage: ionice <pid> [none|rt|be|idle] [prio]\n");
+        return 1;
+    }
+
+    if (!(pid = atoi(argv[1]))) {
+        fprintf(stderr, "Invalid pid specified\n");
+        return 1;
+    }
+
+    if (argc == 2) {
+        if (android_get_ioprio(pid, &clazz, &ioprio)) {
+            fprintf(stderr, "Failed to read priority (%s)\n", strerror(errno));
+            return 1;
+        }
+        fprintf(stdout, "Pid %d, class %s (%d), prio %d\n", pid, classes[clazz], clazz, ioprio);
+        return 0;
+    }
+
+    if (!strcmp(argv[2], "none")) {
+        clazz = IoSchedClass_NONE;
+    } else if (!strcmp(argv[2], "rt")) {
+        clazz = IoSchedClass_RT;
+    } else if (!strcmp(argv[2], "be")) {
+        clazz = IoSchedClass_BE;
+    } else if (!strcmp(argv[2], "idle")) {
+        clazz = IoSchedClass_IDLE;
+    } else {
+        fprintf(stderr, "Unsupported class '%s'\n", argv[2]);
+        return 1;
+    }
+
+    ioprio = atoi(argv[3]);
+
+    printf("Setting pid %d i/o class to %d, prio %d\n", pid, clazz, ioprio);
+    if (android_set_ioprio(pid, clazz, ioprio)) {
+        fprintf(stderr, "Failed to set priority (%s)\n", strerror(errno));
+        return 1;
+    }
+
+    return 0;
+}