DO NOT MERGE - backport Eclair utils for managing cgroups
diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h
index 49f04e5..f749305 100644
--- a/include/arch/darwin-x86/AndroidConfig.h
+++ b/include/arch/darwin-x86/AndroidConfig.h
@@ -257,4 +257,9 @@
  */
 #define HAVE_WRITEV 1
 
+/*
+ * Define if <sched.h> exists.
+ */
+#define HAVE_SCHED_H 1
+
 #endif /*_ANDROID_CONFIG_H*/
diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h
index f322127..72c1374 100644
--- a/include/arch/linux-arm/AndroidConfig.h
+++ b/include/arch/linux-arm/AndroidConfig.h
@@ -302,4 +302,9 @@
  */
 #define HAVE_WRITEV 1
 
+/*
+ * Define if <sched.h> exists.
+ */
+#define HAVE_SCHED_H 1
+
 #endif /* _ANDROID_CONFIG_H */
diff --git a/include/arch/linux-x86/AndroidConfig.h b/include/arch/linux-x86/AndroidConfig.h
index 6de75f8..c6d33c8 100644
--- a/include/arch/linux-x86/AndroidConfig.h
+++ b/include/arch/linux-x86/AndroidConfig.h
@@ -283,4 +283,9 @@
  */
 #define HAVE_WRITEV 1
 
+/*
+ * Define if <sched.h> exists.
+ */
+#define HAVE_SCHED_H 1
+
 #endif /*_ANDROID_CONFIG_H*/
diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h
index 4aa44f8..1d14ef2 100644
--- a/include/arch/target_linux-x86/AndroidConfig.h
+++ b/include/arch/target_linux-x86/AndroidConfig.h
@@ -293,4 +293,9 @@
  */
 #define HAVE_UNWIND_CONTEXT_STRUCT
 
+/*
+ * Define if <sched.h> exists.
+ */
+#define HAVE_SCHED_H 1
+
 #endif /* _ANDROID_CONFIG_H */
diff --git a/include/cutils/sched_policy.h b/include/cutils/sched_policy.h
new file mode 100644
index 0000000..eaf3993
--- /dev/null
+++ b/include/cutils/sched_policy.h
@@ -0,0 +1,36 @@
+/*
+ * 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_SCHED_POLICY_H
+#define __CUTILS_SCHED_POLICY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    SP_BACKGROUND = 0,
+    SP_FOREGROUND = 1,
+} SchedPolicy;
+
+extern int set_sched_policy(int tid, SchedPolicy policy);
+extern int get_sched_policy(int tid, SchedPolicy *policy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_SCHED_POLICY_H */ 
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 18d0ee3..38a2093 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -36,7 +36,8 @@
 	record_stream.c \
 	process_name.c \
 	properties.c \
-	threads.c
+	threads.c \
+	sched_policy.c
 
 # some files must not be compiled when building against Mingw
 # they correspond to features not used by our host development tools
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
new file mode 100644
index 0000000..77b174b
--- /dev/null
+++ b/libcutils/sched_policy.c
@@ -0,0 +1,229 @@
+
+/* libs/cutils/sched_policy.c
+**
+** Copyright 2009, 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>
+
+#define LOG_TAG "SchedPolicy"
+#include "cutils/log.h"
+
+#ifdef HAVE_SCHED_H
+
+#include <sched.h>
+
+#include <cutils/sched_policy.h>
+
+#ifndef SCHED_NORMAL
+  #define SCHED_NORMAL 0
+#endif
+
+#ifndef SCHED_BATCH
+  #define SCHED_BATCH 3
+#endif
+
+#define POLICY_DEBUG 0
+
+static int __sys_supports_schedgroups = -1;
+
+static int add_tid_to_cgroup(int tid, const char *grp_name)
+{
+    int fd;
+    char path[255];
+    char text[64];
+
+    sprintf(path, "/dev/cpuctl/%s/tasks", grp_name);
+
+    if ((fd = open(path, O_WRONLY)) < 0) {
+        LOGE("add_tid_to_cgroup failed to open '%s' (%s)\n", path, strerror(errno));
+        return -1;
+    }
+
+    sprintf(text, "%d", tid);
+    if (write(fd, text, strlen(text)) < 0) {
+        close(fd);
+        /*
+         * If the thread is in the process of exiting,
+         * don't flag an error
+         */
+        if (errno == ESRCH)
+            return 0;
+        LOGW("add_tid_to_cgroup failed to write '%s' (%s)\n", path,
+             strerror(errno));
+        return -1;
+    }
+
+    close(fd);
+    return 0;
+}
+
+static inline void initialize()
+{
+    if (__sys_supports_schedgroups < 0) {
+        if (!access("/dev/cpuctl/tasks", F_OK)) {
+            __sys_supports_schedgroups = 1;
+        } else {
+            __sys_supports_schedgroups = 0;
+        }
+    }
+}
+
+/*
+ * Try to get the scheduler group.
+ *
+ * The data from /proc/<pid>/cgroup looks like:
+ *  2:cpu:/bg_non_interactive
+ *
+ * We return the part after the "/", which will be an empty string for
+ * the default cgroup.  If the string is longer than "bufLen", the string
+ * will be truncated.
+ */
+static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
+{
+#ifdef HAVE_ANDROID_OS
+    char pathBuf[32];
+    char readBuf[256];
+    ssize_t count;
+    int fd;
+
+    snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
+    if ((fd = open(pathBuf, O_RDONLY)) < 0) {
+        return -1;
+    }
+
+    count = read(fd, readBuf, sizeof(readBuf));
+    if (count <= 0) {
+        close(fd);
+        errno = ENODATA;
+        return -1;
+    }
+    close(fd);
+
+    readBuf[--count] = '\0';    /* remove the '\n', now count==strlen */
+
+    char* cp = strchr(readBuf, '/');
+    if (cp == NULL) {
+        readBuf[sizeof(readBuf)-1] = '\0';
+        errno = ENODATA;
+        return -1;
+    }
+
+    memcpy(buf, cp+1, count);   /* count-1 for cp+1, count+1 for NUL */
+    return 0;
+#else
+    errno = ENOSYS;
+    return -1;
+#endif
+}
+
+int get_sched_policy(int tid, SchedPolicy *policy)
+{
+    initialize();
+
+    if (__sys_supports_schedgroups) {
+        char grpBuf[32];
+        if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0)
+            return -1;
+        if (grpBuf[0] == '\0') {
+            *policy = SP_FOREGROUND;
+        } else if (!strcmp(grpBuf, "bg_non_interactive")) {
+            *policy = SP_BACKGROUND;
+        } else {
+            errno = ERANGE;
+            return -1;
+        }
+    } else {
+        int rc = sched_getscheduler(tid);
+        if (rc < 0)
+            return -1;
+        else if (rc == SCHED_NORMAL)
+            *policy = SP_FOREGROUND;
+        else if (rc == SCHED_BATCH)
+            *policy = SP_BACKGROUND;
+        else {
+            errno = ERANGE;
+            return -1;
+        }
+    }
+    return 0;
+}
+
+int set_sched_policy(int tid, SchedPolicy policy)
+{
+    initialize();
+
+#if POLICY_DEBUG
+    char statfile[64];
+    char statline[1024];
+    char thread_name[255];
+    int fd;
+
+    sprintf(statfile, "/proc/%d/stat", tid);
+    memset(thread_name, 0, sizeof(thread_name));
+
+    fd = open(statfile, O_RDONLY);
+    if (fd >= 0) {
+        int rc = read(fd, statline, 1023);
+        close(fd);
+        statline[rc] = 0;
+        char *p = statline;
+        char *q;
+
+        for (p = statline; *p != '('; p++);
+        p++;
+        for (q = p; *q != ')'; q++);
+
+        strncpy(thread_name, p, (q-p));
+    }
+    if (policy == SP_BACKGROUND) {
+        LOGD("vvv tid %d (%s)", tid, thread_name);
+    } else if (policy == SP_FOREGROUND) {
+        LOGD("^^^ tid %d (%s)", tid, thread_name);
+    } else {
+        LOGD("??? tid %d (%s)", tid, thread_name);
+    }
+#endif
+
+    if (__sys_supports_schedgroups) {
+        const char *grp = "";
+
+        if (policy == SP_BACKGROUND) {
+            grp = "bg_non_interactive";
+        }
+
+        if (add_tid_to_cgroup(tid, grp)) {
+            if (errno != ESRCH && errno != ENOENT)
+                return -errno;
+        }
+    } else {
+        struct sched_param param;
+
+        param.sched_priority = 0;
+        sched_setscheduler(tid,
+                           (policy == SP_BACKGROUND) ?
+                            SCHED_BATCH : SCHED_NORMAL,
+                           &param);
+    }
+
+    return 0;
+}
+
+#endif /* HAVE_SCHED_H */
diff --git a/toolbox/ps.c b/toolbox/ps.c
index 3b86fa2..bc50cfa 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -11,6 +11,8 @@
 
 #include <pwd.h>
 
+#include <cutils/sched_policy.h>
+
 
 static char *nexttoksep(char **strp, char *sep)
 {
@@ -24,6 +26,7 @@
 
 #define SHOW_PRIO 1
 #define SHOW_TIME 2
+#define SHOW_POLICY 4
 
 static int display_flags = 0;
 
@@ -138,12 +141,26 @@
     }
     
     if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) {
-        printf("%-8s %-5d %-5d %-5d %-5d", user, pid, ppid, vss / 1024, rss * 4);
+        printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
         if(display_flags&SHOW_PRIO)
             printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
+        if (display_flags & SHOW_POLICY) {
+            SchedPolicy p;
+            if (get_sched_policy(pid, &p) < 0)
+                printf(" un ");
+            else {
+                if (p == SP_BACKGROUND)
+                    printf(" bg ");
+                else if (p == SP_FOREGROUND)
+                    printf(" fg ");
+                else
+                    printf(" er ");
+            }
+        }
         printf(" %08x %08x %s %s", wchan, eip, state, cmdline[0] ? cmdline : name);
         if(display_flags&SHOW_TIME)
             printf(" (u:%d, s:%d)", utime, stime);
+
         printf("\n");
     }
     return 0;
@@ -186,6 +203,8 @@
             threads = 1;
         } else if(!strcmp(argv[1],"-x")) {
             display_flags |= SHOW_TIME;
+        } else if(!strcmp(argv[1],"-P")) {
+            display_flags |= SHOW_POLICY;
         } else if(!strcmp(argv[1],"-p")) {
             display_flags |= SHOW_PRIO;
         }  else if(isdigit(argv[1][0])){
@@ -197,8 +216,9 @@
         argv++;
     }
 
-    printf("USER     PID   PPID  VSIZE RSS   %sWCHAN    PC         NAME\n", 
-           (display_flags&SHOW_PRIO)?"PRIO  NICE  RTPRI SCHED ":"");
+    printf("USER     PID   PPID  VSIZE  RSS   %s %s WCHAN    PC         NAME\n", 
+           (display_flags&SHOW_PRIO)?"PRIO  NICE  RTPRI SCHED ":"",
+           (display_flags&SHOW_POLICY)?"PCY " : "");
     while((de = readdir(d)) != 0){
         if(isdigit(de->d_name[0])){
             int pid = atoi(de->d_name);