Skip to content

Commit 858491b

Browse files
author
Jeff Brown
committed
Synthesize fake vsyncs when the screen is off.
When the screen is off, we might not receive real vsync pulses from the hardware which would cause posted Choreographer callbacks to not run. This is bad because messages in the Looper might be blocked behind a barrier that is scheduled to be removed by one of those Choreographer callback (see ViewRootImpl.doTraversals). Until the barrier is removed, those messages will not run. To prevent starvation of the Looper, we synthesize fake vsync pulses at a reduced rate whenever the display hardware stops generating them. This change should fix a variety of rare non-deterministic bugs where the system might appear to be unresponsive while the screen is off, and spurious ANRs reported shortly after the screen is turned back on. Bug: 6574842 Bug: 6636995 Bug: 6643559 Change-Id: I263f2fdf979afd79e5ac47a0cc5d34a93b860c21
1 parent 82134f7 commit 858491b

File tree

1 file changed

+36
-6
lines changed

1 file changed

+36
-6
lines changed

core/java/android/view/Choreographer.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ public final class Choreographer {
7979
// be dequeued.
8080
private static final long DEFAULT_FRAME_DELAY = 10;
8181

82+
// The fake vsync delay in milliseconds.
83+
// When the screen is off, we might not receive real vsync pulses from the hardware
84+
// which would cause posted Choreographer callbacks to not run. This is bad because
85+
// messages in the Looper might be blocked behind a barrier that is scheduled to be
86+
// removed by one of those Choreographer callback (see ViewRootImpl.doTraversals).
87+
// Until the barrier is removed, those messages will not run. To prevent starvation
88+
// of the Looper, we synthesize fake vsync pulses at a reduced rate whenever the
89+
// display hardware stops generating them.
90+
private static final long FAKE_VSYNC_DELAY = 100;
91+
8292
// The number of milliseconds between animation frames.
8393
private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
8494

@@ -113,6 +123,7 @@ protected Choreographer initialValue() {
113123
private static final int MSG_DO_FRAME = 0;
114124
private static final int MSG_DO_SCHEDULE_VSYNC = 1;
115125
private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
126+
private static final int MSG_FAKE_VSYNC = 3;
116127

117128
// All frame callbacks posted by applications have this token.
118129
private static final Object FRAME_CALLBACK_TOKEN = new Object() {
@@ -587,6 +598,13 @@ void doScheduleCallback(int callbackType) {
587598

588599
private void scheduleVsyncLocked() {
589600
mDisplayEventReceiver.scheduleVsync();
601+
602+
// Post a message to simulate a fake vsync pulse at a reduced rate in case the
603+
// display hardware stops generating them. This ensures that Choreographer
604+
// callbacks can continue to run even if the screen is off.
605+
Message msg = mHandler.obtainMessage(MSG_FAKE_VSYNC);
606+
msg.setAsynchronous(true);
607+
mHandler.sendMessageDelayed(msg, FAKE_VSYNC_DELAY);
590608
}
591609

592610
private boolean isRunningOnLooperThreadLocked() {
@@ -662,6 +680,12 @@ public void handleMessage(Message msg) {
662680
case MSG_DO_SCHEDULE_CALLBACK:
663681
doScheduleCallback(msg.arg1);
664682
break;
683+
case MSG_FAKE_VSYNC:
684+
if (DEBUG) {
685+
Log.d(TAG, "Handling fake vsync while screen is off.");
686+
}
687+
doFrame(System.nanoTime(), 0);
688+
break;
665689
}
666690
}
667691
}
@@ -683,6 +707,17 @@ public void onVsync(long timestampNanos, int frame) {
683707
// the message queue. If there are no messages in the queue with timestamps
684708
// earlier than the frame time, then the vsync event will be processed immediately.
685709
// Otherwise, messages that predate the vsync event will be handled first.
710+
if (mHavePendingVsync) {
711+
if (DEBUG) {
712+
Log.d(TAG, "Already have a pending vsync event. There should only be "
713+
+ "one at a time but they can double up when a fake vsync "
714+
+ "is handled in place of a real one.");
715+
}
716+
mHandler.removeCallbacks(this);
717+
} else {
718+
mHavePendingVsync = true;
719+
}
720+
686721
long now = System.nanoTime();
687722
if (timestampNanos > now) {
688723
Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
@@ -691,12 +726,7 @@ public void onVsync(long timestampNanos, int frame) {
691726
timestampNanos = now;
692727
}
693728

694-
if (mHavePendingVsync) {
695-
Log.w(TAG, "Already have a pending vsync event. There should only be "
696-
+ "one at a time.");
697-
} else {
698-
mHavePendingVsync = true;
699-
}
729+
mHandler.removeMessages(MSG_FAKE_VSYNC);
700730

701731
mTimestampNanos = timestampNanos;
702732
mFrame = frame;

0 commit comments

Comments
 (0)