Propagate background scheduling class across processes.

This is a very simply implementation: upon receiving an IPC, if the handling
thread is at a background priority (the driver will have taken care of
propagating this from the calling thread), then stick it in to the background
scheduling group.  Plus an API to turn this off for the process, which is
used by the system process.

This also pulls some of the code for managing scheduling classes out of
the Process JNI wrappers and in to some convenience methods in thread.h.
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index eacf0ce..ba0bf0d 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -76,6 +76,13 @@
      */
     public static final native IBinder getContextObject();
     
+    /**
+     * Special for system process to not allow incoming calls to run at
+     * background scheduling priority.
+     * @hide
+     */
+    public static final native void disableBackgroundScheduling(boolean disable);
+    
     static native final void handleGc();
     
     public static void forceGc(String reason) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index f0885fd..627fcbf 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -670,6 +670,12 @@
     android::IPCThreadState::self()->joinThreadPool();
 }
 
+static void android_os_BinderInternal_disableBackgroundScheduling(JNIEnv* env,
+        jobject clazz, jboolean disable)
+{
+    IPCThreadState::disableBackgroundScheduling(disable ? true : false);
+}
+
 static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz)
 {
     LOGV("Gc has executed, clearing binder ops");
@@ -682,6 +688,7 @@
      /* name, signature, funcPtr */
     { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
     { "joinThreadPool", "()V", (void*)android_os_BinderInternal_joinThreadPool },
+    { "disableBackgroundScheduling", "(Z)V", (void*)android_os_BinderInternal_disableBackgroundScheduling },
     { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc }
 };
 
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 094b02d..e84f2e5 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -120,11 +120,7 @@
 
 jint android_os_Process_myTid(JNIEnv* env, jobject clazz)
 {
-#ifdef HAVE_GETTID
-    return gettid();
-#else
-    return getpid();
-#endif
+    return androidGetTid();
 }
 
 jint android_os_Process_getUidForName(JNIEnv* env, jobject clazz, jstring name)
@@ -191,15 +187,11 @@
 
 void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp)
 {
-    if (grp > ANDROID_TGROUP_MAX || grp < 0) { 
-        signalExceptionForGroupError(env, clazz, EINVAL);
+    int res = androidSetThreadSchedulingGroup(pid, grp);
+    if (res != NO_ERROR) {
+        signalExceptionForGroupError(env, clazz, res == BAD_VALUE ? EINVAL : errno);
         return;
     }
-
-    if (set_sched_policy(pid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
-                                      SP_BACKGROUND : SP_FOREGROUND)) {
-        signalExceptionForGroupError(env, clazz, errno);
-    }
 }
 
 void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp) 
@@ -275,22 +267,15 @@
 void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
                                               jint pid, jint pri)
 {
-    int rc = 0;
-
-    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
-        rc = set_sched_policy(pid, SP_BACKGROUND);
-    } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) {
-        rc = set_sched_policy(pid, SP_FOREGROUND);
+    int rc = androidSetThreadPriority(pid, pri);
+    if (rc != 0) {
+        if (rc == INVALID_OPERATION) {
+            signalExceptionForPriorityError(env, clazz, errno);
+        } else {
+            signalExceptionForGroupError(env, clazz, errno);
+        }
     }
-
-    if (rc) {
-        signalExceptionForGroupError(env, clazz, errno);
-        return;
-    }
-
-    if (setpriority(PRIO_PROCESS, pid, pri) < 0) {
-        signalExceptionForPriorityError(env, clazz, errno);
-    }
+    
     //LOGI("Setting priority of %d: %d, getpriority returns %d\n",
     //     pid, pri, getpriority(PRIO_PROCESS, pid));
 }
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
index 884b5c1..749a977 100644
--- a/include/binder/IBinder.h
+++ b/include/binder/IBinder.h
@@ -52,7 +52,7 @@
         DUMP_TRANSACTION        = B_PACK_CHARS('_','D','M','P'),
         INTERFACE_TRANSACTION   = B_PACK_CHARS('_', 'N', 'T', 'F'),
 
-        // Corresponds to tfOneWay -- an asynchronous call.
+        // Corresponds to TF_ONE_WAY -- an asynchronous call.
         FLAG_ONEWAY             = 0x00000001
     };
 
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
index 78306b2..3ab985d 100644
--- a/include/binder/IPCThreadState.h
+++ b/include/binder/IPCThreadState.h
@@ -68,6 +68,13 @@
 
     static  void                shutdown();
     
+    // Call this to disable switching threads to background scheduling when
+    // receiving incoming IPC calls.  This is specifically here for the
+    // Android system process, since it expects to have background apps calling
+    // in to it but doesn't want to acquire locks in its services while in
+    // the background.
+    static  void                disableBackgroundScheduling(bool disable);
+    
 private:
                                 IPCThreadState();
                                 ~IPCThreadState();
@@ -93,9 +100,10 @@
                                            void* cookie);
     
     const   sp<ProcessState>    mProcess;
+    const   pid_t               mMyThreadId;
             Vector<BBinder*>    mPendingStrongDerefs;
             Vector<RefBase::weakref_type*> mPendingWeakDerefs;
-                                
+            
             Parcel              mIn;
             Parcel              mOut;
             status_t            mLastError;
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 0fc533f..130d83c 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -124,6 +124,24 @@
 
 extern void androidSetCreateThreadFunc(android_create_thread_fn func);
 
+// ------------------------------------------------------------------
+// Extra functions working with raw pids.
+
+// Get pid for the current thread.
+extern pid_t androidGetTid();
+
+// Change the scheduling group of a particular thread.  The group
+// should be one of the ANDROID_TGROUP constants.  Returns BAD_VALUE if
+// grp is out of range, else another non-zero value with errno set if
+// the operation failed.
+extern int androidSetThreadSchedulingGroup(pid_t tid, int grp);
+
+// Change the priority AND scheduling group of a particular thread.  The priority
+// should be one of the ANDROID_PRIORITY constants.  Returns INVALID_OPERATION
+// if the priority set failed, else another value if just the group set failed;
+// in either case errno is set.
+extern int androidSetThreadPriority(pid_t tid, int prio);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index b2a7db8..473f580 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -292,6 +292,7 @@
 static bool gHaveTLS = false;
 static pthread_key_t gTLS = 0;
 static bool gShutdown = false;
+static bool gDisableBackgroundScheduling = false;
 
 IPCThreadState* IPCThreadState::self()
 {
@@ -332,6 +333,11 @@
     }
 }
 
+void IPCThreadState::disableBackgroundScheduling(bool disable)
+{
+    gDisableBackgroundScheduling = disable;
+}
+
 sp<ProcessState> IPCThreadState::process()
 {
     return mProcess;
@@ -386,6 +392,11 @@
 
     mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
     
+    // This thread may have been spawned by a thread that was in the background
+    // scheduling group, so first we will make sure it is in the default/foreground
+    // one to avoid performing an initial transaction in the background.
+    androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);
+        
     status_t result;
     do {
         int32_t cmd;
@@ -427,19 +438,13 @@
         }
         
         // After executing the command, ensure that the thread is returned to the
-        // default cgroup and priority before rejoining the pool.  This is a failsafe
-        // in case the command implementation failed to properly restore the thread's
-        // scheduling parameters upon completion.
-        int my_id;
-#ifdef HAVE_GETTID
-        my_id = gettid();
-#else
-        my_id = getpid();
-#endif
-        if (!set_sched_policy(my_id, SP_FOREGROUND)) {
-            // success; reset the priority as well
-            setpriority(PRIO_PROCESS, my_id, ANDROID_PRIORITY_NORMAL);
-        }
+        // default cgroup before rejoining the pool.  The driver takes care of
+        // restoring the priority, but doesn't do anything with cgroups so we
+        // need to take care of that here in userspace.  Note that we do make
+        // sure to go in the foreground after executing a transaction, but
+        // there are other callbacks into user code that could have changed
+        // our group so we want to make absolutely sure it is put back.
+        androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);
 
         // Let this thread exit the thread pool if it is no longer
         // needed and it is not the main process thread.
@@ -583,10 +588,10 @@
 }
 
 IPCThreadState::IPCThreadState()
-    : mProcess(ProcessState::self())
+    : mProcess(ProcessState::self()), mMyThreadId(androidGetTid())
 {
     pthread_setspecific(gTLS, this);
-        clearCaller();
+    clearCaller();
     mIn.setDataCapacity(256);
     mOut.setDataCapacity(256);
 }
@@ -930,6 +935,17 @@
             mCallingPid = tr.sender_pid;
             mCallingUid = tr.sender_euid;
             
+            bool doBackground = !gDisableBackgroundScheduling &&
+                    getpriority(PRIO_PROCESS, mMyThreadId)
+                            >= ANDROID_PRIORITY_BACKGROUND;
+            if (doBackground) {
+                // We have inherited a background priority from the caller.
+                // Ensure this thread is in the background scheduling class,
+                // since the driver won't modify scheduling classes for us.
+                androidSetThreadSchedulingGroup(mMyThreadId,
+                        ANDROID_TGROUP_BG_NONINTERACT);
+            }
+            
             //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
             
             Parcel reply;
@@ -967,6 +983,13 @@
             mCallingPid = origPid;
             mCallingUid = origUid;
             
+            if (doBackground) {
+                // We moved to the background scheduling group to execute
+                // this transaction, so now that we are done go back in the
+                // foreground.
+                androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);
+            }
+            
             IF_LOG_TRANSACTIONS() {
                 TextOutput::Bundle _b(alog);
                 alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index ec3db09..6ca2603 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -20,6 +20,8 @@
 #include <utils/threads.h>
 #include <utils/Log.h>
 
+#include <cutils/sched_policy.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <memory.h>
@@ -269,6 +271,53 @@
     gCreateThreadFn = func;
 }
 
+pid_t androidGetTid()
+{
+#ifdef HAVE_GETTID
+    return gettid();
+#else
+    return getpid();
+#endif
+}
+
+int androidSetThreadSchedulingGroup(pid_t tid, int grp)
+{
+    if (grp > ANDROID_TGROUP_MAX || grp < 0) { 
+        return BAD_VALUE;
+    }
+
+    if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
+                                      SP_BACKGROUND : SP_FOREGROUND)) {
+        return PERMISSION_DENIED;
+    }
+    
+    return NO_ERROR;
+}
+
+int androidSetThreadPriority(pid_t tid, int pri)
+{
+    int rc = 0;
+    int lasterr = 0;
+
+    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+        rc = set_sched_policy(tid, SP_BACKGROUND);
+    } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
+        rc = set_sched_policy(tid, SP_FOREGROUND);
+    }
+
+    if (rc) {
+        lasterr = errno;
+    }
+
+    if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
+        rc = INVALID_OPERATION;
+    } else {
+        errno = lasterr;
+    }
+    
+    return rc;
+}
+
 namespace android {
 
 /*
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 73f930d..214ecc1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -18,6 +18,7 @@
 
 import com.android.server.am.ActivityManagerService;
 import com.android.server.status.StatusBarService;
+import com.android.internal.os.BinderInternal;
 import com.android.internal.os.SamplingProfilerIntegration;
 
 import dalvik.system.VMRuntime;
@@ -80,6 +81,8 @@
         android.os.Process.setThreadPriority(
                 android.os.Process.THREAD_PRIORITY_FOREGROUND);
 
+        BinderInternal.disableBackgroundScheduling(true);
+        
         String factoryTestStr = SystemProperties.get("ro.factorytest");
         int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF
                 : Integer.parseInt(factoryTestStr);