Do not hold WM lock while closing animation transaction

Animation transactions can be blocking, leading to total
window manager starvation. Fix this by not holding lock.

Test: Double tap recents button, make sure it's always responsive
Change-Id: I8e09e04f243d2bfc09fb68097846a42e76c7cab5
Fixes: 38192114
This commit is contained in:
Jorim Jaggi
2017-05-11 16:27:06 +02:00
parent 152f6c8e04
commit 9b19fd425d

View File

@@ -115,8 +115,8 @@ public class WindowAnimator {
mAnimationTick = () -> { mAnimationTick = () -> {
synchronized (mService.mWindowMap) { synchronized (mService.mWindowMap) {
mAnimationTickScheduled = false; mAnimationTickScheduled = false;
animateLocked(mCurrentFrameTime);
} }
animate(mCurrentFrameTime);
}; };
mAnimationFrameCallback = frameTimeNs -> { mAnimationFrameCallback = frameTimeNs -> {
synchronized (mService.mWindowMap) { synchronized (mService.mWindowMap) {
@@ -126,8 +126,8 @@ public class WindowAnimator {
return; return;
} }
mAnimationTickScheduled = true; mAnimationTickScheduled = true;
mSfChoreographer.scheduleAtSfVsync(mAnimationTick);
} }
mSfChoreographer.scheduleAtSfVsync(mAnimationTick);
}; };
} }
@@ -151,25 +151,34 @@ public class WindowAnimator {
mDisplayContentsAnimators.delete(displayId); mDisplayContentsAnimators.delete(displayId);
} }
/** Locked on mService.mWindowMap. */ /**
private void animateLocked(long frameTimeNs) { * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes
* an animation transaction, that might be blocking until the next sf-vsync, so we want to make
* sure other threads can make progress if this happens.
*/
private void animate(long frameTimeNs) {
boolean transactionOpen = false;
boolean wasAnimating = false;
try {
synchronized (mService.mWindowMap) {
if (!mInitialized) { if (!mInitialized) {
return; return;
} }
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS; mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE; mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
boolean wasAnimating = mAnimating; wasAnimating = mAnimating;
setAnimating(false); setAnimating(false);
mAppWindowAnimating = false; mAppWindowAnimating = false;
if (DEBUG_WINDOW_TRACE) { if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime); Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
} }
if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animateLocked"); if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animate");
mService.openSurfaceTransaction(); mService.openSurfaceTransaction();
transactionOpen = true;
SurfaceControl.setAnimationTransaction(); SurfaceControl.setAnimationTransaction();
try {
final AccessibilityController accessibilityController = final AccessibilityController accessibilityController =
mService.mAccessibilityController; mService.mAccessibilityController;
final int numDisplays = mDisplayContentsAnimators.size(); final int numDisplays = mDisplayContentsAnimators.size();
@@ -189,10 +198,11 @@ public class WindowAnimator {
screenRotationAnimation.kill(); screenRotationAnimation.kill();
displayAnimator.mScreenRotationAnimation = null; displayAnimator.mScreenRotationAnimation = null;
//TODO (multidisplay): Accessibility supported only for the default display. //TODO (multidisplay): Accessibility supported only for the default
// display.
if (accessibilityController != null && dc.isDefaultDisplay) { if (accessibilityController != null && dc.isDefaultDisplay) {
// We just finished rotation animation which means we did not announce // We just finished rotation animation which means we did not
// the rotation and waited for it to end, announce now. // announce the rotation and waited for it to end, announce now.
accessibilityController.onRotationChangedLocked( accessibilityController.onRotationChangedLocked(
mService.getDefaultDisplayContentLocked()); mService.getDefaultDisplayContentLocked());
} }
@@ -238,13 +248,17 @@ public class WindowAnimator {
if (mService.mWatermark != null) { if (mService.mWatermark != null) {
mService.mWatermark.drawIfNeeded(); mService.mWatermark.drawIfNeeded();
} }
}
} catch (RuntimeException e) { } catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e); Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally { } finally {
if (transactionOpen) {
mService.closeSurfaceTransaction(); mService.closeSurfaceTransaction();
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animateLocked"); if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
}
} }
synchronized (mService.mWindowMap) {
boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this); boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
boolean doRequest = false; boolean doRequest = false;
if (mBulkUpdateParams != 0) { if (mBulkUpdateParams != 0) {
@@ -257,8 +271,9 @@ public class WindowAnimator {
if (mAnimating && !wasAnimating) { if (mAnimating && !wasAnimating) {
// Usually app transitions but quite a load onto the system already (with all the things // Usually app transitions but quite a load onto the system already (with all the
// happening in app), so pause task snapshot persisting to not increase the load. // things happening in app), so pause task snapshot persisting to not increase the
// load.
mService.mTaskSnapshotController.setPersisterPaused(true); mService.mTaskSnapshotController.setPersisterPaused(true);
if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0); Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
@@ -289,6 +304,7 @@ public class WindowAnimator {
+ Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY))); + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
} }
} }
}
private static String bulkUpdateParamsToString(int bulkUpdateParams) { private static String bulkUpdateParamsToString(int bulkUpdateParams) {
StringBuilder builder = new StringBuilder(128); StringBuilder builder = new StringBuilder(128);