Add support for changing a threads scheduler group. Three groups are available (default, background non interactive, foreground boost). Setting a thread priority to PRIORITY_BACKGROUND will transparently change groups to background

Signed-off-by: San Mehat <san@google.com>
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e4412a3..30acef9 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -176,6 +176,26 @@
      */
     public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1;
 
+    /**
+     * Default thread group - gets a 'normal' share of the CPU
+     * @hide
+     */
+    public static final int THREAD_GROUP_DEFAULT = 0;
+
+    /**
+     * Background non-interactive thread group - All threads in
+     * this group are scheduled with a reduced share of the CPU.
+     * @hide
+     */
+    public static final int THREAD_GROUP_BG_NONINTERACTIVE = 1;
+
+    /**
+     * Foreground 'boost' thread group - All threads in
+     * this group are scheduled with an increased share of the CPU
+     * @hide
+     **/
+    public static final int THREAD_GROUP_FG_BOOST = 2;
+
     public static final int SIGNAL_QUIT = 3;
     public static final int SIGNAL_KILL = 9;
     public static final int SIGNAL_USR1 = 10;
@@ -569,6 +589,21 @@
      */
     public static final native void setThreadPriority(int tid, int priority)
             throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Sets the scheduling group for a thread.
+     * @hide
+     * @param tid The indentifier of the thread/process to change.
+     * @param group The target group for this thread/process.
+     * 
+     * @throws IllegalArgumentException Throws IllegalArgumentException if
+     * <var>tid</var> does not exist.
+     * @throws SecurityException Throws SecurityException if your process does
+     * not have permission to modify the given thread, or to use the given
+     * priority.
+     */
+    public static final native void setThreadGroup(int tid, int group)
+            throws IllegalArgumentException, SecurityException;
     
     /**
      * Set the priority of the calling thread, based on Linux priorities.  See
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index bd56605..d760feb 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -50,6 +50,16 @@
 #undef __KERNEL__
 #endif
 
+#define ENABLE_CGROUP_ERR_LOGGING 0
+
+/*
+ * List of cgroup names which map to ANDROID_TGROUP_ values in Thread.h
+ * and Process.java
+ * These names are used to construct the path to the cgroup control dir
+ */
+
+static const char *cgroup_names[] = { NULL, "bg_non_interactive", "fg_boost" };
+
 using namespace android;
 
 static void signalExceptionForPriorityError(JNIEnv* env, jobject obj, int err)
@@ -73,6 +83,28 @@
     }
 }
 
+static void signalExceptionForGroupError(JNIEnv* env, jobject obj, int err)
+{
+    switch (err) {
+        case EINVAL:
+            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+            break;
+        case ESRCH:
+            jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist");
+            break;
+        case EPERM:
+            jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread");
+            break;
+        case EACCES:
+            jniThrowException(env, "java/lang/SecurityException", "No permission to set to given group");
+            break;
+        default:
+            jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
+            break;
+    }
+}
+
+
 static void fakeProcessEntry(void* arg)
 {
     String8* cls = (String8*)arg;
@@ -164,9 +196,55 @@
     return -1;
 }
 
+static int add_pid_to_cgroup(int pid, int grp)
+{
+    FILE *fp;
+    char path[255];
+    int rc;
+
+    sprintf(path, "/dev/cpuctl/%s/tasks", (cgroup_names[grp] ? cgroup_names[grp] : ""));
+
+    if (!(fp = fopen(path, "w"))) {
+#if ENABLE_CGROUP_ERR_LOGGING
+        LOGW("Unable to open %s (%s)\n", path, strerror(errno));
+#endif
+        return -errno;
+    }
+
+    rc = fprintf(fp, "%d", pid);
+    fclose(fp);
+
+    if (rc < 0) {
+#if ENABLE_CGROUP_ERR_LOGGING
+        LOGW("Unable to move pid %d to cgroup %s (%s)\n", pid,
+             (cgroup_names[grp] ? cgroup_names[grp] : "<default>"),
+             strerror(errno));
+#endif
+    }
+
+    return (rc < 0) ? errno : 0;
+}
+
+void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp)
+{
+    if (grp > ANDROID_TGROUP_MAX || grp < 0) { 
+        signalExceptionForGroupError(env, clazz, EINVAL);
+        return;
+    }
+
+    if (add_pid_to_cgroup(pid, grp))
+        signalExceptionForGroupError(env, clazz, errno);
+}
+
 void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
                                               jint pid, jint pri)
 {
+    if (pri == ANDROID_PRIORITY_BACKGROUND) {
+        add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT);
+    } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) {
+        add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT);
+    }
+
     if (setpriority(PRIO_PROCESS, pid, pri) < 0) {
         signalExceptionForPriorityError(env, clazz, errno);
     }
@@ -741,6 +819,7 @@
     {"setThreadPriority",   "(II)V", (void*)android_os_Process_setThreadPriority},
     {"setThreadPriority",   "(I)V", (void*)android_os_Process_setCallingThreadPriority},
     {"getThreadPriority",   "(I)I", (void*)android_os_Process_getThreadPriority},
+    {"setThreadGroup",      "(II)V", (void*)android_os_Process_setThreadGroup},
     {"setOomAdj",   "(II)Z", (void*)android_os_Process_setOomAdj},
     {"setArgV0",    "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
     {"setUid", "(I)I", (void*)android_os_Process_setUid},
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 8d8d46a..b320915 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -79,6 +79,13 @@
     ANDROID_PRIORITY_LESS_FAVORABLE = +1,
 };
 
+enum {
+    ANDROID_TGROUP_DEFAULT          = 0,
+    ANDROID_TGROUP_BG_NONINTERACT   = 1,
+    ANDROID_TGROUP_FG_BOOST         = 2,
+    ANDROID_TGROUP_MAX              = ANDROID_TGROUP_FG_BOOST,
+};
+
 // Create and run a new thread.
 extern int androidCreateThread(android_thread_func_t, void *);