Add new mode for SCHED_FIFO on UI and RenderThreads.

Add a new mode, controlled by sys.use_fifo_ui property, that enables the
top app's UI and RenderThread to be SCHED_FIFO. This eliminates almost
all jank due to scheduling competition with non-UI critical
threads. This mode may not be suitable for all devices.

bug 24503801

Change-Id: I7b8a31830ad80f7efa00236928d5476998ed4e00
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 14f9db7..277348a 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -3003,6 +3003,13 @@
             reply.writeNoException();
             return true;
         }
+        case SET_RENDER_THREAD_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final int tid = data.readInt();
+            setRenderThread(tid);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -7040,7 +7047,7 @@
 
     @Override
     public void setVrThread(int tid)
-            throws RemoteException {
+        throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -7052,5 +7059,18 @@
         return;
     }
 
+    public void setRenderThread(int tid)
+        throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(tid);
+        mRemote.transact(SET_RENDER_THREAD_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+        return;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index d38fb941..4a4202a 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -659,6 +659,7 @@
             throws RemoteException;
 
     public void setVrThread(int tid) throws RemoteException;
+    public void setRenderThread(int tid) throws RemoteException;
 
     /*
      * Private non-Binder interfaces
@@ -1046,5 +1047,8 @@
     int START_CONFIRM_DEVICE_CREDENTIAL_INTENT = IBinder.FIRST_CALL_TRANSACTION + 374;
     int SEND_IDLE_JOB_TRIGGER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 375;
     int SEND_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 376;
+
+    // Start of N MR1 transactions
     int SET_VR_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 377;
+    int SET_RENDER_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 378;
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 24e7a63..c26d974 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -329,7 +329,6 @@
      */
     public static final int SCHED_RESET_ON_FORK = 0x40000000;
 
-
     // Keep in sync with SP_* constants of enum type SchedPolicy
     // declared in system/core/include/cutils/sched_policy.h,
     // except THREAD_GROUP_DEFAULT does not correspond to any SP_* value.
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index fcca739..e129a06 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.app.ActivityManagerNative;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.Context;
@@ -917,10 +918,20 @@
         synchronized void init(Context context, long renderProxy) {
             if (mInitialized) return;
             mInitialized = true;
+            initSched(context, renderProxy);
             initGraphicsStats(context, renderProxy);
             initAssetAtlas(context, renderProxy);
         }
 
+        private static void initSched(Context context, long renderProxy) {
+            try {
+                int tid = nGetRenderThreadTid(renderProxy);
+                ActivityManagerNative.getDefault().setRenderThread(tid);
+            } catch (Throwable t) {
+                Log.w(LOG_TAG, "Failed to set scheduler for RenderThread", t);
+            }
+        }
+
         private static void initGraphicsStats(Context context, long renderProxy) {
             try {
                 IBinder binder = ServiceManager.getService("graphicsstats");
@@ -979,6 +990,7 @@
 
     private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
     private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
+    private static native int nGetRenderThreadTid(long nativeProxy);
 
     private static native long nCreateRootRenderNode();
     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 61a0bda..595d5c6 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -543,6 +543,12 @@
     proxy->setProcessStatsBuffer(fd);
 }
 
+static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    return proxy->getRenderThreadTid();
+}
+
 static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
     RootRenderNode* node = new RootRenderNode(env);
     node->incStrong(0);
@@ -858,6 +864,7 @@
 static const JNINativeMethod gMethods[] = {
     { "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V",   (void*) android_view_ThreadedRenderer_setAtlas },
     { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
+    { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
     { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
     { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 54af282..06a24b2 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -514,6 +514,10 @@
     post(task);
 }
 
+int RenderProxy::getRenderThreadTid() {
+    return mRenderThread.getTid();
+}
+
 CREATE_BRIDGE3(addRenderNode, CanvasContext* context, RenderNode* node, bool placeFront) {
     args->context->addRenderNode(args->node, args->placeFront);
     return nullptr;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 898b314..e31062c8 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -115,6 +115,7 @@
 
     ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
     ANDROID_API void setProcessStatsBuffer(int fd);
+    ANDROID_API int getRenderThreadTid();
 
     ANDROID_API void serializeDisplayListTree();
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9f4bb56..08cf22b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -569,6 +569,9 @@
     private boolean mShowDialogs = true;
     private boolean mInVrMode = false;
 
+    // Whether we should use SCHED_FIFO for UI and RenderThreads.
+    private boolean mUseFifoUiScheduling = false;
+
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
     // Convenient for easy iteration over the queues. Foreground is first
@@ -2657,6 +2660,10 @@
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
 
+        if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0) {
+            mUseFifoUiScheduling = true;
+        }
+
         mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
 
         mConfiguration.setToDefaults();
@@ -12533,6 +12540,10 @@
                 final int pid = Binder.getCallingPid();
                 proc = mPidsSelfLocked.get(pid);
                 if (proc != null && mInVrMode && tid >= 0) {
+                    // ensure the tid belongs to the process
+                    if (!Process.isThreadInProcess(pid, tid)) {
+                        throw new IllegalArgumentException("VR thread does not belong to process");
+                    }
                     // reset existing VR thread to CFS
                     if (proc.vrThreadTid != 0) {
                         Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0);
@@ -12557,10 +12568,11 @@
             synchronized (mPidsSelfLocked) {
                 int pid = Binder.getCallingPid();
                 proc = mPidsSelfLocked.get(pid);
-                if (proc != null && proc.renderThreadTid == 0 && tid > 0) {
+                if (mUseFifoUiScheduling && proc != null && proc.renderThreadTid == 0 && tid > 0) {
                     // ensure the tid belongs to the process
-                    if (Process.isThreadInProcess(pid, tid) == false) {
-                        return;
+                    if (!Process.isThreadInProcess(pid, tid)) {
+                        throw new IllegalArgumentException(
+                            "Render thread does not belong to process");
                     }
                     proc.renderThreadTid = tid;
                     if (DEBUG_OOM_ADJ) {
@@ -12574,7 +12586,9 @@
                     }
                 } else {
                     if (DEBUG_OOM_ADJ) {
-                        Slog.d("UI_FIFO", "Didn't set thread from setRenderThreadForPid?");
+                        Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? " +
+                               "PID: " + pid + ", TID: " + tid + " FIFO: " +
+                               mUseFifoUiScheduling);
                     }
                 }
             }
@@ -19579,7 +19593,7 @@
                             adj = ProcessList.FOREGROUND_APP_ADJ;
                             if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
                                 if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
-                                    schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+                                    schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
                                 } else {
                                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                                 }
@@ -20196,47 +20210,65 @@
                         processGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
                         break;
                     case ProcessList.SCHED_GROUP_TOP_APP:
+                    case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
                         processGroup = Process.THREAD_GROUP_TOP_APP;
                         break;
                     default:
                         processGroup = Process.THREAD_GROUP_DEFAULT;
                         break;
                 }
-                if (true) {
-                    long oldId = Binder.clearCallingIdentity();
-                    try {
-                        Process.setProcessGroup(app.pid, processGroup);
-                        if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
-                            // do nothing if we already switched to RT
-                            if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                                // Switch VR thread for app to SCHED_FIFO
-                                if (mInVrMode && app.vrThreadTid != 0) {
-                                    Process.setThreadScheduler(app.vrThreadTid,
+                long oldId = Binder.clearCallingIdentity();
+                try {
+                    Process.setProcessGroup(app.pid, processGroup);
+                    if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+                        // do nothing if we already switched to RT
+                        if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+                            // Switch VR thread for app to SCHED_FIFO
+                            if (mInVrMode && app.vrThreadTid != 0) {
+                                Process.setThreadScheduler(app.vrThreadTid,
+                                    Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+                            }
+                            if (mUseFifoUiScheduling) {
+                                // Switch UI pipeline for app to SCHED_FIFO
+                                app.savedPriority = Process.getThreadPriority(app.pid);
+                                Process.setThreadScheduler(app.pid,
+                                    Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+                                if (app.renderThreadTid != 0) {
+                                    Process.setThreadScheduler(app.renderThreadTid,
                                         Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+                                    if (DEBUG_OOM_ADJ) {
+                                        Slog.d("UI_FIFO", "Set RenderThread (TID " +
+                                            app.renderThreadTid + ") to FIFO");
+                                    }
+                                } else {
+                                    if (DEBUG_OOM_ADJ) {
+                                        Slog.d("UI_FIFO", "Not setting RenderThread TID");
+                                    }
                                 }
                             }
-                        } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
-                                   app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                            // Reset VR thread to SCHED_OTHER
-                            // Safe to do even if we're not in VR mode
-                            if (app.vrThreadTid != 0) {
-                                Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0);
-                            }
                         }
-                    } catch (Exception e) {
-                        Slog.w(TAG, "Failed setting process group of " + app.pid
-                                + " to " + app.curSchedGroup);
-                        e.printStackTrace();
-                    } finally {
-                        Binder.restoreCallingIdentity(oldId);
-                    }
-                } else {
-                    if (app.thread != null) {
-                        try {
-                            app.thread.setSchedulingGroup(processGroup);
-                        } catch (RemoteException e) {
+                    } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
+                               app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+                        // Reset VR thread to SCHED_OTHER
+                        // Safe to do even if we're not in VR mode
+                        if (app.vrThreadTid != 0) {
+                            Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0);
+                        }
+                        if (mUseFifoUiScheduling) {
+                            // Reset UI pipeline to SCHED_OTHER
+                            Process.setThreadScheduler(app.pid, Process.SCHED_OTHER, 0);
+                            Process.setThreadScheduler(app.renderThreadTid,
+                                Process.SCHED_OTHER, 0);
+                            Process.setThreadPriority(app.pid, app.savedPriority);
+                            Process.setThreadPriority(app.renderThreadTid, -4);
                         }
                     }
+                } catch (Exception e) {
+                    Slog.w(TAG, "Failed setting process group of " + app.pid
+                            + " to " + app.curSchedGroup);
+                    e.printStackTrace();
+                } finally {
+                    Binder.restoreCallingIdentity(oldId);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f073e5c..475b155 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -130,6 +130,9 @@
     static final int SCHED_GROUP_DEFAULT = 1;
     // Activity manager's version of Process.THREAD_GROUP_TOP_APP
     static final int SCHED_GROUP_TOP_APP = 2;
+    // Activity manager's version of Process.THREAD_GROUP_TOP_APP
+    // Disambiguate between actual top app and processes bound to the top app
+    static final int SCHED_GROUP_TOP_APP_BOUND = 3;
 
     // The minimum number of cached apps we want to be able to keep around,
     // without empty apps being able to push them out of memory.
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 0f7c89d..dad383d 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -103,6 +103,8 @@
     int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
     int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
     int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
+    int savedPriority;          // Previous priority value if we're switching to non-SCHED_OTHER
+    int renderThreadTid;        // TID for RenderThread
     boolean serviceb;           // Process currently is on the service B list
     boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
     boolean setIsForeground;    // Running foreground UI when last set?