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?