diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 649accd1126dd..8954a1eaf1469 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -384,4 +384,9 @@ interface IWindowSession { * Clears a touchable region set by {@link #setInsets}. */ void clearTouchableRegion(IWindow window); + + /** + * Returns whether this window needs to cancel draw and retry later. + */ + boolean cancelDraw(IWindow window); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f1976319794a7..e6882d180735c 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -84,6 +84,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; +import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW; import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER; @@ -601,6 +602,14 @@ public final class ViewRootImpl implements ViewParent, */ private boolean mSyncBuffer = false; + /** + * Flag to determine whether the client needs to check with WMS if it can draw. WMS will notify + * the client that it can't draw if we're still in the middle of a sync set that includes this + * window. Once the sync is complete, the window can resume drawing. This is to ensure we don't + * deadlock the client by trying to request draws when there may not be any buffers available. + */ + private boolean mCheckIfCanDraw = false; + int mSyncSeqId = 0; int mLastSyncSeqId = 0; @@ -2700,6 +2709,9 @@ public final class ViewRootImpl implements ViewParent, mIsInTraversal = true; mWillDrawSoon = true; + boolean cancelDraw = false; + boolean isSyncRequest = false; + boolean windowSizeMayChange = false; WindowManager.LayoutParams lp = mWindowAttributes; @@ -2969,6 +2981,8 @@ public final class ViewRootImpl implements ViewParent, mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED; } relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); + cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW) + == RELAYOUT_RES_CANCEL_AND_REDRAW; final boolean dragResizing = mPendingDragResizing; if (mSyncSeqId > mLastSyncSeqId) { mLastSyncSeqId = mSyncSeqId; @@ -2977,6 +2991,7 @@ public final class ViewRootImpl implements ViewParent, } reportNextDraw(); mSyncBuffer = true; + isSyncRequest = true; } final boolean surfaceControlChanged = @@ -3265,6 +3280,19 @@ public final class ViewRootImpl implements ViewParent, } } } else { + // If a relayout isn't going to happen, we still need to check if this window can draw + // when mCheckIfCanDraw is set. This is because it means we had a sync in the past, but + // have not been told by WMS that the sync is complete and that we can continue to draw + if (mCheckIfCanDraw) { + try { + cancelDraw = mWindowSession.cancelDraw(mWindow); + if (DEBUG_BLAST) { + Log.d(mTag, "cancelDraw returned " + cancelDraw); + } + } catch (RemoteException e) { + } + } + // Not the first pass and no window/insets/visibility change but the window // may have moved and we need check that and if so to update the left and right // in the attach info. We translate only the window frame since on window move @@ -3483,7 +3511,9 @@ public final class ViewRootImpl implements ViewParent, reportNextDraw(); } - boolean cancelAndRedraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw(); + mCheckIfCanDraw = isSyncRequest || cancelDraw; + + boolean cancelAndRedraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || cancelDraw; if (!cancelAndRedraw) { createSyncIfNeeded(); } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 85a5762f7f3d0..25445abefca20 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -82,6 +82,11 @@ public final class WindowManagerGlobal { */ public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 1 << 3; + /** + * The window manager has told the window it cannot draw this frame and should retry again. + */ + public static final int RELAYOUT_RES_CANCEL_AND_REDRAW = 1 << 4; + /** * Flag for relayout: the client will be later giving * internal insets; as a result, the window will not impact other window diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index a212348a2315a..5832347c1947c 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -552,4 +552,9 @@ public class WindowlessWindowManager implements IWindowSession { } } } + + @Override + public boolean cancelDraw(IWindow window) { + return false; + } } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 30b50839cd35d..8d03e2e9632ab 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -249,6 +249,11 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { mService.setWillReplaceWindows(appToken, childrenOnly); } + @Override + public boolean cancelDraw(IWindow window) { + return mService.cancelDraw(this, window); + } + @Override public int relayout(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5d2e34bb1b284..c2513662786e7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -89,6 +89,7 @@ import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManagerGlobal.ADD_OKAY; +import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; @@ -2212,6 +2213,20 @@ public class WindowManagerService extends IWindowManager.Stub == PackageManager.PERMISSION_GRANTED; } + /** + * Returns whether this window can proceed with drawing or needs to retry later. + */ + public boolean cancelDraw(Session session, IWindow client) { + synchronized (mGlobalLock) { + final WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + return false; + } + + return win.cancelAndRedraw(); + } + } + public int relayoutWindow(Session session, IWindow client, LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, int flags, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, @@ -2228,6 +2243,11 @@ public class WindowManagerService extends IWindowManager.Stub if (win == null) { return 0; } + + if (win.cancelAndRedraw()) { + result |= RELAYOUT_RES_CANCEL_AND_REDRAW; + } + final DisplayContent displayContent = win.getDisplayContent(); final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); @@ -2530,6 +2550,7 @@ public class WindowManagerService extends IWindowManager.Stub win.mLastSeqIdSentToRelayout = win.mSyncSeqId; outSyncIdBundle.putInt("seqid", win.mSyncSeqId); + win.mAlreadyRequestedSync = true; } else { outSyncIdBundle.putInt("seqid", -1); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 74e15cf08c692..342b04f135c70 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -391,6 +391,7 @@ class WindowState extends WindowContainer implements WindowManagerP */ int mSyncSeqId = 0; int mLastSeqIdSentToRelayout = 0; + boolean mAlreadyRequestedSync; /** * {@code true} when the client was still drawing for sync when the sync-set was finished or @@ -4406,6 +4407,8 @@ class WindowState extends WindowContainer implements WindowManagerP pw.println(prefix + "Requested visibilities: " + visibilityString); } } + + pw.println(prefix + "mAlreadyRequestedSync=" + mAlreadyRequestedSync); } @Override @@ -5933,6 +5936,7 @@ class WindowState extends WindowContainer implements WindowManagerP if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mRedrawForSyncReported) { mClientWasDrawingForSync = true; } + mAlreadyRequestedSync = false; super.finishSync(outMergedTransaction, cancel); } @@ -6199,4 +6203,8 @@ class WindowState extends WindowContainer implements WindowManagerP @WindowTraceLogLevel int logLevel) { dumpDebug(proto, fieldId, logLevel); } + + public boolean cancelAndRedraw() { + return mSyncState != SYNC_STATE_NONE && mAlreadyRequestedSync; + } }