Merge \\"Add a new API to improve VR thread scheduling.\\" into nyc-mr1-dev am: 5e261441d3
am: 75c6078c8b

Change-Id: Ic101c2d17e98fcb1cc2d6e3cccebfb6e378f9914
diff --git a/api/current.txt b/api/current.txt
index 60fa3c2..62be08c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3707,6 +3707,7 @@
     method public void moveTaskToFront(int, int);
     method public void moveTaskToFront(int, int, android.os.Bundle);
     method public deprecated void restartPackage(java.lang.String);
+    method public static void setVrThread(int);
     method public void setWatchHeapLimit(long);
     field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
     field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index 814d6cf..758c2ef 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3843,6 +3843,7 @@
     method public void moveTaskToFront(int, int);
     method public void moveTaskToFront(int, int, android.os.Bundle);
     method public deprecated void restartPackage(java.lang.String);
+    method public static void setVrThread(int);
     method public void setWatchHeapLimit(long);
     field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
     field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
diff --git a/api/test-current.txt b/api/test-current.txt
index 3702023..49dc118 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3707,6 +3707,7 @@
     method public void moveTaskToFront(int, int);
     method public void moveTaskToFront(int, int, android.os.Bundle);
     method public deprecated void restartPackage(java.lang.String);
+    method public static void setVrThread(int);
     method public void setWatchHeapLimit(long);
     field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
     field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 12661ee..22a1fce 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3626,6 +3626,24 @@
     }
 
     /**
+     * Enable more aggressive scheduling for latency-sensitive low-runtime VR threads. Only one
+     * thread can be a VR thread in a process at a time, and that thread may be subject to
+     * restrictions on the amount of time it can run.
+     *
+     * To reset the VR thread for an application, a tid of 0 can be passed.
+     *
+     * @see android.os.Process#myTid()
+     * @param tid tid of the VR thread
+     */
+    public static void setVrThread(int tid) {
+        try {
+            ActivityManagerNative.getDefault().setVrThread(tid);
+        } catch (RemoteException e) {
+            // pass
+        }
+    }
+
+    /**
      * The AppTask allows you to manage your own application's tasks.
      * See {@link android.app.ActivityManager#getAppTasks()}
      */
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 9e9dabb..14f9db7 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2996,6 +2996,13 @@
             reply.writeInt(result);
             return true;
         }
+        case SET_VR_THREAD_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final int tid = data.readInt();
+            setVrThread(tid);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -7031,5 +7038,19 @@
         return res;
     }
 
+    @Override
+    public void setVrThread(int tid)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(tid);
+        mRemote.transact(SET_VR_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 4570f1f..d38fb94 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -658,6 +658,8 @@
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options)
             throws RemoteException;
 
+    public void setVrThread(int tid) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -1044,4 +1046,5 @@
     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;
+    int SET_VR_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 377;
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6aa9fac..4f3388f 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -297,6 +297,13 @@
      */
     public static final int SCHED_IDLE = 5;
 
+    /**
+     * Reset scheduler choice on fork.
+     * @hide
+     */
+    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/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 58bbf8d..415b183 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2310,6 +2310,20 @@
                     if (mInVrMode != vrMode) {
                         mInVrMode = vrMode;
                         mShowDialogs = shouldShowDialogs(mConfiguration, mInVrMode);
+                        if (r.app != null) {
+                            ProcessRecord proc = r.app;
+                            if (proc.vrThreadTid > 0) {
+                                if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+                                    if (mInVrMode == true) {
+                                        Process.setThreadScheduler(proc.vrThreadTid,
+                                            Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+                                    } else {
+                                        Process.setThreadScheduler(proc.vrThreadTid,
+                                            Process.SCHED_OTHER, 0);
+                                    }
+                                }
+                            }
+                        }
                     }
                 }
                 vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
@@ -12508,6 +12522,34 @@
         }
     }
 
+    public void setVrThread(int tid) {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+            throw new UnsupportedOperationException("VR mode not supported on this device!");
+        }
+
+        synchronized (this) {
+            ProcessRecord proc;
+            synchronized (mPidsSelfLocked) {
+                final int pid = Binder.getCallingPid();
+                proc = mPidsSelfLocked.get(pid);
+                if (proc != null && mInVrMode && tid >= 0) {
+                    // reset existing VR thread to CFS
+                    if (proc.vrThreadTid != 0) {
+                        Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0);
+                    }
+                    // add check to guarantee that tid belongs to pid?
+                    proc.vrThreadTid = tid;
+                    // promote to FIFO now if the tid is non-zero
+                    if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && proc.vrThreadTid > 0) {
+                        Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+                    }
+                } else {
+                    //Slog.e("VR_FIFO", "Didn't set thread from setVrThread?");
+                }
+            }
+        }
+    }
+
     @Override
     public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
@@ -20111,6 +20153,7 @@
         }
 
         if (app.setSchedGroup != app.curSchedGroup) {
+            int oldSchedGroup = app.setSchedGroup;
             app.setSchedGroup = app.curSchedGroup;
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
                     "Setting sched group of " + app.processName
@@ -20136,6 +20179,23 @@
                     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);
+                                }
+                            }
+                        } 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);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 8911a3e..0f7c89d 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -97,6 +97,7 @@
     int verifiedAdj;            // The last adjustment that was verified as actually being set
     int curSchedGroup;          // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
+    int vrThreadTid;            // Thread currently set for VR scheduling
     int trimMemoryLevel;        // Last selected memory trimming level
     int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
     int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
@@ -293,6 +294,7 @@
                 pw.print(" setSchedGroup="); pw.print(setSchedGroup);
                 pw.print(" systemNoUi="); pw.print(systemNoUi);
                 pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
+        pw.print(prefix); pw.print("vrThreadTid="); pw.print(vrThreadTid);
         pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
                 pw.print(" repProcState="); pw.print(repProcState);
                 pw.print(" pssProcState="); pw.print(pssProcState);