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,142 +151,158 @@ 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
if (!mInitialized) { * an animation transaction, that might be blocking until the next sf-vsync, so we want to make
return; * sure other threads can make progress if this happens.
} */
private void animate(long frameTimeNs) {
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS; boolean transactionOpen = false;
mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE; boolean wasAnimating = false;
boolean wasAnimating = mAnimating;
setAnimating(false);
mAppWindowAnimating = false;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animateLocked");
mService.openSurfaceTransaction();
SurfaceControl.setAnimationTransaction();
try { try {
final AccessibilityController accessibilityController = synchronized (mService.mWindowMap) {
mService.mAccessibilityController; if (!mInitialized) {
final int numDisplays = mDisplayContentsAnimators.size(); return;
for (int i = 0; i < numDisplays; i++) { }
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
dc.stepAppWindowsAnimation(mCurrentTime);
DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
final ScreenRotationAnimation screenRotationAnimation = mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
displayAnimator.mScreenRotationAnimation; mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) { wasAnimating = mAnimating;
if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) { setAnimating(false);
setAnimating(true); mAppWindowAnimating = false;
} else { if (DEBUG_WINDOW_TRACE) {
mBulkUpdateParams |= SET_UPDATE_ROTATION; Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
screenRotationAnimation.kill(); }
displayAnimator.mScreenRotationAnimation = null;
//TODO (multidisplay): Accessibility supported only for the default display. if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animate");
if (accessibilityController != null && dc.isDefaultDisplay) { mService.openSurfaceTransaction();
// We just finished rotation animation which means we did not announce transactionOpen = true;
// the rotation and waited for it to end, announce now. SurfaceControl.setAnimationTransaction();
accessibilityController.onRotationChangedLocked(
mService.getDefaultDisplayContentLocked()); final AccessibilityController accessibilityController =
mService.mAccessibilityController;
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
dc.stepAppWindowsAnimation(mCurrentTime);
DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
final ScreenRotationAnimation screenRotationAnimation =
displayAnimator.mScreenRotationAnimation;
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
setAnimating(true);
} else {
mBulkUpdateParams |= SET_UPDATE_ROTATION;
screenRotationAnimation.kill();
displayAnimator.mScreenRotationAnimation = null;
//TODO (multidisplay): Accessibility supported only for the default
// display.
if (accessibilityController != null && dc.isDefaultDisplay) {
// We just finished rotation animation which means we did not
// announce the rotation and waited for it to end, announce now.
accessibilityController.onRotationChangedLocked(
mService.getDefaultDisplayContentLocked());
}
} }
} }
// Update animations of all applications, including those
// associated with exiting/removed apps
++mAnimTransactionSequence;
dc.updateWindowsForAnimator(this);
dc.updateWallpaperForAnimator(this);
dc.prepareWindowSurfaces();
}
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
dc.checkAppWindowsReadyToShow();
final ScreenRotationAnimation screenRotationAnimation =
mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
if (screenRotationAnimation != null) {
screenRotationAnimation.updateSurfacesInTransaction();
}
orAnimating(dc.animateDimLayers());
orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
//TODO (multidisplay): Magnification is supported only for the default display.
if (accessibilityController != null && dc.isDefaultDisplay) {
accessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
}
} }
// Update animations of all applications, including those if (mService.mDragState != null) {
// associated with exiting/removed apps mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime);
++mAnimTransactionSequence;
dc.updateWindowsForAnimator(this);
dc.updateWallpaperForAnimator(this);
dc.prepareWindowSurfaces();
}
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
dc.checkAppWindowsReadyToShow();
final ScreenRotationAnimation screenRotationAnimation =
mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
if (screenRotationAnimation != null) {
screenRotationAnimation.updateSurfacesInTransaction();
} }
orAnimating(dc.animateDimLayers()); if (mAnimating) {
orAnimating(dc.getDockedDividerController().animate(mCurrentTime)); mService.scheduleAnimationLocked();
//TODO (multidisplay): Magnification is supported only for the default display.
if (accessibilityController != null && dc.isDefaultDisplay) {
accessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
} }
}
if (mService.mDragState != null) { if (mService.mWatermark != null) {
mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime); mService.mWatermark.drawIfNeeded();
} }
if (mAnimating) {
mService.scheduleAnimationLocked();
}
if (mService.mWatermark != null) {
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 {
mService.closeSurfaceTransaction(); if (transactionOpen) {
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animateLocked"); mService.closeSurfaceTransaction();
} if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
boolean doRequest = false;
if (mBulkUpdateParams != 0) {
doRequest = mService.mRoot.copyAnimToLayoutParams();
}
if (hasPendingLayoutChanges || doRequest) {
mWindowPlacerLocked.requestTraversal();
}
if (mAnimating && !wasAnimating) {
// Usually app transitions but quite a load onto the system already (with all the things
// happening in app), so pause task snapshot persisting to not increase the load.
mService.mTaskSnapshotController.setPersisterPaused(true);
if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
} }
} }
if (!mAnimating && wasAnimating) { synchronized (mService.mWindowMap) {
mWindowPlacerLocked.requestTraversal(); boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
mService.mTaskSnapshotController.setPersisterPaused(false); boolean doRequest = false;
if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { if (mBulkUpdateParams != 0) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0); doRequest = mService.mRoot.copyAnimToLayoutParams();
} }
}
if (mRemoveReplacedWindows) { if (hasPendingLayoutChanges || doRequest) {
mService.mRoot.removeReplacedWindows(); mWindowPlacerLocked.requestTraversal();
mRemoveReplacedWindows = false; }
}
mService.stopUsingSavedSurfaceLocked(); if (mAnimating && !wasAnimating) {
mService.destroyPreservedSurfaceLocked();
mService.mWindowPlacerLocked.destroyPendingSurfaces();
if (DEBUG_WINDOW_TRACE) { // Usually app transitions but quite a load onto the system already (with all the
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating // things happening in app), so pause task snapshot persisting to not increase the
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams) // load.
+ " mPendingLayoutChanges(DEFAULT_DISPLAY)=" mService.mTaskSnapshotController.setPersisterPaused(true);
+ Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY))); if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
}
}
if (!mAnimating && wasAnimating) {
mWindowPlacerLocked.requestTraversal();
mService.mTaskSnapshotController.setPersisterPaused(false);
if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
}
}
if (mRemoveReplacedWindows) {
mService.mRoot.removeReplacedWindows();
mRemoveReplacedWindows = false;
}
mService.stopUsingSavedSurfaceLocked();
mService.destroyPreservedSurfaceLocked();
mService.mWindowPlacerLocked.destroyPendingSurfaces();
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ " mPendingLayoutChanges(DEFAULT_DISPLAY)="
+ Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
}
} }
} }