From 0303c5723edfdb4f2db37543544d7cbe9b14df97 Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Thu, 20 Oct 2016 10:16:29 -0700 Subject: [PATCH 1/3] Have DisplayContent be the call point for adjusting wallpaper windows 4th step in trying to make the WindowList private to DisplayContent Have the rest of the system call DisplayContent when they need to adjust the position of the wallpaper windows in the window list instead of calling WallpaperController directly. That way the display content can control the window list that the wallpaper controller sees. Test: Existing tests pass and manual testing. Change-Id: Iaa7f421d7cd24d36e5a83e091f77b4a08d0ae123 --- .../com/android/server/wm/DisplayContent.java | 14 ++++++-- .../server/wm/RootWindowContainer.java | 30 ++++++++-------- .../java/com/android/server/wm/Session.java | 10 +++--- .../server/wm/WallpaperController.java | 11 +++--- .../server/wm/WindowManagerService.java | 35 +++++++++---------- .../com/android/server/wm/WindowState.java | 10 +++--- .../server/wm/WindowStateAnimator.java | 2 +- .../server/wm/WindowSurfacePlacer.java | 9 +++-- .../com/android/server/wm/WindowToken.java | 6 ++-- 9 files changed, 67 insertions(+), 60 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fab0b29b14295..cefa4f932f294 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -202,6 +202,7 @@ class DisplayContent extends WindowContainer @@ -1888,7 +1898,7 @@ class DisplayContent extends WindowContainer unForceHiding = null; WindowState wallpaper = null; - final WallpaperController wallpaperController = mService.mWallpaperControllerLocked; + final WallpaperController wallpaperController = mWallpaperController; for (int i = mWindows.size() - 1; i >= 0; i--) { WindowState win = mWindows.get(i); WindowStateAnimator winAnimator = win.mWinAnimator; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index bddf6e2e915eb..24a0d1aff9969 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -166,10 +166,12 @@ class RootWindowContainer extends WindowContainer { RemoteEventTrace mRemoteEventTrace; private final WindowLayersController mLayersController; + final WallpaperController mWallpaperController; RootWindowContainer(WindowManagerService service) { mService = service; mLayersController = new WindowLayersController(mService); + mWallpaperController = new WallpaperController(mService); } WindowState computeFocusedWindow() { @@ -214,7 +216,8 @@ class RootWindowContainer extends WindowContainer { } private DisplayContent createDisplayContent(final Display display) { - final DisplayContent dc = new DisplayContent(display, mService, mLayersController); + final DisplayContent dc = new DisplayContent(display, mService, mLayersController, + mWallpaperController); final int displayId = display.getDisplayId(); if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display); @@ -442,10 +445,10 @@ class RootWindowContainer extends WindowContainer { // TODO(multi-display): By default we add this to the default display, but maybe we // should provide an API for a token to be added to any display? - final WindowToken token = new WindowToken(mService, binder, type, true, - getDisplayContent(DEFAULT_DISPLAY)); + final DisplayContent dc = getDisplayContent(DEFAULT_DISPLAY); + final WindowToken token = new WindowToken(mService, binder, type, true, dc); if (type == TYPE_WALLPAPER) { - mService.mWallpaperControllerLocked.addWallpaperToken(token); + dc.mWallpaperController.addWallpaperToken(token); } } @@ -718,7 +721,6 @@ class RootWindowContainer extends WindowContainer { boolean addPipInputConsumerHandle = pipInputConsumer != null; boolean addWallpaperInputConsumerHandle = wallpaperInputConsumer != null; final Rect pipTouchableBounds = addPipInputConsumerHandle ? new Rect() : null; - final WallpaperController wallpaperController = mService.mWallpaperControllerLocked; boolean disableWallpaperTouchEvents = false; final int count = mChildren.size(); @@ -769,7 +771,7 @@ class RootWindowContainer extends WindowContainer { if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) { disableWallpaperTouchEvents = true; } - final boolean hasWallpaper = wallpaperController.isWallpaperTarget(child) + final boolean hasWallpaper = dc.mWallpaperController.isWallpaperTarget(child) && (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0 && !disableWallpaperTouchEvents; @@ -951,14 +953,13 @@ class RootWindowContainer extends WindowContainer { "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces"); } - final WindowList defaultWindows = defaultDisplay.getWindowList(); final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; // If we are ready to perform an app transition, check through all of the app tokens to be // shown and see if they are ready to go. if (mService.mAppTransition.isReady()) { defaultDisplay.pendingLayoutChanges |= - surfacePlacer.handleAppTransitionReadyLocked(defaultWindows); + surfacePlacer.handleAppTransitionReadyLocked(); if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("after handleAppTransitionReadyLocked", defaultDisplay.pendingLayoutChanges); @@ -1046,7 +1047,7 @@ class RootWindowContainer extends WindowContainer { if (mService.mInputMethodWindow == win) { mService.mInputMethodWindow = null; } - if (mService.mWallpaperControllerLocked.isWallpaperTarget(win)) { + if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) { wallpaperDestroyed = true; } win.destroyOrSaveSurface(); @@ -1063,7 +1064,7 @@ class RootWindowContainer extends WindowContainer { if (!token.hasVisible) { exitingTokens.remove(i); if (token.windowType == TYPE_WALLPAPER) { - mService.mWallpaperControllerLocked.removeWallpaperToken(token); + displayContent.mWallpaperController.removeWallpaperToken(token); } } } @@ -1251,9 +1252,8 @@ class RootWindowContainer extends WindowContainer { if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats( "On entry to LockedInner", dc.pendingLayoutChanges); - if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0 - && mService.mWallpaperControllerLocked.adjustWallpaperWindows()) { - dc.assignWindowLayers(true /*setLayoutNeeded*/); + if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) { + dc.adjustWallpaperWindows(); } if (isDefaultDisplay @@ -1318,10 +1318,10 @@ class RootWindowContainer extends WindowContainer { w.applyDimLayerIfNeeded(); if (isDefaultDisplay && obscuredChanged && w.isVisibleLw() - && mService.mWallpaperControllerLocked.isWallpaperTarget(w)) { + && dc.mWallpaperController.isWallpaperTarget(w)) { // This is the wallpaper target and its obscured state changed... make sure the // current wallpaper's visibility has been updated accordingly. - mService.mWallpaperControllerLocked.updateWallpaperVisibility(); + dc.mWallpaperController.updateWallpaperVisibility(); } w.handleWindowMovedIfNeeded(); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 80092074bc29c..ead70e184e5c8 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -460,7 +460,7 @@ public class Session extends IWindowSession.Stub synchronized(mService.mWindowMap) { long ident = Binder.clearCallingIdentity(); try { - mService.mWallpaperControllerLocked.setWindowWallpaperPosition( + mService.mRoot.mWallpaperController.setWindowWallpaperPosition( mService.windowForClientLocked(this, window, true), x, y, xStep, yStep); } finally { @@ -471,7 +471,7 @@ public class Session extends IWindowSession.Stub public void wallpaperOffsetsComplete(IBinder window) { synchronized (mService.mWindowMap) { - mService.mWallpaperControllerLocked.wallpaperOffsetsComplete(window); + mService.mRoot.mWallpaperController.wallpaperOffsetsComplete(window); } } @@ -479,7 +479,7 @@ public class Session extends IWindowSession.Stub synchronized(mService.mWindowMap) { long ident = Binder.clearCallingIdentity(); try { - mService.mWallpaperControllerLocked.setWindowWallpaperDisplayOffset( + mService.mRoot.mWallpaperController.setWindowWallpaperDisplayOffset( mService.windowForClientLocked(this, window, true), x, y); } finally { Binder.restoreCallingIdentity(ident); @@ -492,7 +492,7 @@ public class Session extends IWindowSession.Stub synchronized(mService.mWindowMap) { long ident = Binder.clearCallingIdentity(); try { - return mService.mWallpaperControllerLocked.sendWindowWallpaperCommand( + return mService.mRoot.mWallpaperController.sendWindowWallpaperCommand( mService.windowForClientLocked(this, window, true), action, x, y, z, extras, sync); } finally { @@ -503,7 +503,7 @@ public class Session extends IWindowSession.Stub public void wallpaperCommandComplete(IBinder window, Bundle result) { synchronized (mService.mWindowMap) { - mService.mWallpaperControllerLocked.wallpaperCommandComplete(window); + mService.mRoot.mWallpaperController.wallpaperCommandComplete(window); } } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 972a0cb05a380..4ab88877fc2b3 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -628,10 +628,9 @@ class WallpaperController { return changed; } - boolean adjustWallpaperWindows() { + boolean adjustWallpaperWindows(WindowList windows) { mService.mRoot.mWallpaperMayChange = false; - final WindowList windows = mService.getDefaultWindowListLocked(); // First find top-most window that has asked to be on top of the wallpaper; // all wallpapers go behind it. findWallpaperTarget(windows, mFindResults); @@ -726,8 +725,8 @@ class WallpaperController { * Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of * the opening apps should be a wallpaper target. */ - void adjustWallpaperWindowsForAppTransitionIfNeeded( - DisplayContent dc, ArraySet openingApps, WindowList windows) { + void adjustWallpaperWindowsForAppTransitionIfNeeded(DisplayContent dc, + ArraySet openingApps) { boolean adjust = false; if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) { adjust = true; @@ -741,8 +740,8 @@ class WallpaperController { } } - if (adjust && adjustWallpaperWindows()) { - dc.assignWindowLayers(true /*setLayoutNeeded*/); + if (adjust) { + dc.adjustWallpaperWindows(); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index aa5f98aaaa0c9..01b143bb11628 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -684,8 +684,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - WallpaperController mWallpaperControllerLocked; - boolean mAnimateWallpaperWithTarget; // TODO: Move to RootWindowContainer @@ -979,7 +977,6 @@ public class WindowManagerService extends IWindowManager.Stub mDisplaySettings = new DisplaySettings(); mDisplaySettings.readSettingsLocked(); - mWallpaperControllerLocked = new WallpaperController(this); mWindowPlacerLocked = new WindowSurfacePlacer(this); mPolicy = policy; @@ -1397,11 +1394,11 @@ public class WindowManagerService extends IWindowManager.Stub } else { win.mToken.addWindow(win); if (type == TYPE_WALLPAPER) { - mWallpaperControllerLocked.clearLastWallpaperTimeoutTime(); + displayContent.mWallpaperController.clearLastWallpaperTimeoutTime(); displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } else if (mWallpaperControllerLocked.isBelowWallpaperTarget(win)) { + } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) { // If there is currently a wallpaper being shown, and // the base layer of the new window is below the current // layer of the target window, then adjust the wallpaper. @@ -1675,14 +1672,14 @@ public class WindowManagerService extends IWindowManager.Stub atoken.postWindowRemoveStartingWindowCleanup(win); } + final DisplayContent dc = win.getDisplayContent(); if (win.mAttrs.type == TYPE_WALLPAPER) { - mWallpaperControllerLocked.clearLastWallpaperTimeoutTime(); - getDefaultDisplayContentLocked().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + dc.mWallpaperController.clearLastWallpaperTimeoutTime(); + dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } else if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { - getDefaultDisplayContentLocked().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } - final DisplayContent dc = win.getDisplayContent(); if (dc != null && dc.removeFromWindowList(win)) { if (!mWindowPlacerLocked.isInLayout()) { dc.assignWindowLayers(true /* setLayoutNeeded */); @@ -2079,7 +2076,7 @@ public class WindowManagerService extends IWindowManager.Stub mWindowPlacerLocked.performSurfacePlacement(); if (toBeDisplayed && win.mIsWallpaper) { DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); - mWallpaperControllerLocked.updateWallpaperOffset( + dc.mWallpaperController.updateWallpaperOffset( win, displayInfo.logicalWidth, displayInfo.logicalHeight, false); } if (win.mAppToken != null) { @@ -2147,7 +2144,7 @@ public class WindowManagerService extends IWindowManager.Stub // an exit. win.mAnimatingExit = true; win.mWinAnimator.mAnimating = true; - } else if (mWallpaperControllerLocked.isWallpaperTarget(win)) { + } else if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) { // If the wallpaper is currently behind this // window, we need to change both of them inside // of a transaction to avoid artifacts. @@ -2418,7 +2415,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowToken wtoken = removedTokens.get(i); wtoken.setExiting(); if (wtoken.windowType == TYPE_WALLPAPER) { - mWallpaperControllerLocked.removeWallpaperToken(wtoken); + wtoken.getDisplayContent().mWallpaperController.removeWallpaperToken(wtoken); } mInputMonitor.updateInputWindowsLw(true /*force*/); @@ -3035,7 +3032,8 @@ public class WindowManagerService extends IWindowManager.Stub return false; } if (windowShowWallpaper) { - if (mWallpaperControllerLocked.getWallpaperTarget() == null) { + if (wtoken.getDisplayContent().mWallpaperController.getWallpaperTarget() + == null) { // If this theme is requesting a wallpaper, and the wallpaper // is not currently visible, then this effectively serves as // an opaque window and our starting window transition animation @@ -7004,7 +7002,7 @@ public class WindowManagerService extends IWindowManager.Stub break; case WALLPAPER_DRAW_PENDING_TIMEOUT: { synchronized (mWindowMap) { - if (mWallpaperControllerLocked.processWallpaperDrawPendingTimeout()) { + if (mRoot.mWallpaperController.processWallpaperDrawPendingTimeout()) { mWindowPlacerLocked.performSurfacePlacement(); } } @@ -7628,10 +7626,11 @@ public class WindowManagerService extends IWindowManager.Stub } mNoAnimationNotifyOnTransitionFinished.clear(); - mWallpaperControllerLocked.hideDeferredWallpapersIfNeeded(); - // TODO: multi-display. final DisplayContent dc = getDefaultDisplayContentLocked(); + + dc.mWallpaperController.hideDeferredWallpapersIfNeeded(); + dc.onAppTransitionDone(); changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; @@ -8229,7 +8228,7 @@ public class WindowManagerService extends IWindowManager.Stub private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) { pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)"); mRoot.dumpTokens(pw, dumpAll); - mWallpaperControllerLocked.dumpTokens(pw, " ", dumpAll); + mRoot.mWallpaperController.dumpTokens(pw, " ", dumpAll); if (!mFinishedStarting.isEmpty()) { pw.println(); pw.println(" Finishing start of application tokens:"); @@ -8413,7 +8412,7 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mInputMethodWindow="); pw.println(mInputMethodWindow); } mWindowPlacerLocked.dump(pw, " "); - mWallpaperControllerLocked.dump(pw, " "); + mRoot.mWallpaperController.dump(pw, " "); pw.print(" mSystemBooted="); pw.print(mSystemBooted); pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 1fe6d5e163839..f80e08543e654 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -942,7 +942,7 @@ class WindowState extends WindowContainer implements WindowManagerP final DisplayContent displayContent = getDisplayContent(); if (displayContent != null) { final DisplayInfo displayInfo = displayContent.getDisplayInfo(); - mService.mWallpaperControllerLocked.updateWallpaperOffset( + getDisplayContent().mWallpaperController.updateWallpaperOffset( this, displayInfo.logicalWidth, displayInfo.logicalHeight, false); } } @@ -1969,7 +1969,7 @@ class WindowState extends WindowContainer implements WindowManagerP if (mIsImWindow) { specialAdjustment = getDisplayContent().mInputMethodAnimLayerAdjustment; } else if (mIsWallpaper) { - specialAdjustment = mService.mWallpaperControllerLocked.getAnimLayerAdjustment(); + specialAdjustment = getDisplayContent().mWallpaperController.getAnimLayerAdjustment(); } return mLayer + specialAdjustment; @@ -2551,7 +2551,7 @@ class WindowState extends WindowContainer implements WindowManagerP clearAnimatingWithSavedSurface(); mDestroying = true; mWinAnimator.hide("stopUsingSavedSurface"); - mService.mWallpaperControllerLocked.hideWallpapers(this); + getDisplayContent().mWallpaperController.hideWallpapers(this); } void markSavedSurfaceExiting() { @@ -3959,7 +3959,7 @@ class WindowState extends WindowContainer implements WindowManagerP } } mAnimatingExit = false; - mService.mWallpaperControllerLocked.hideWallpapers(this); + getDisplayContent().mWallpaperController.hideWallpapers(this); } boolean clearAnimatingFlags() { @@ -4026,7 +4026,7 @@ class WindowState extends WindowContainer implements WindowManagerP */ void dispatchWallpaperVisibility(final boolean visible) { final boolean hideAllowed = - mService.mWallpaperControllerLocked.mDeferredHideWallpaper == null; + getDisplayContent().mWallpaperController.mDeferredHideWallpaper == null; // Only send notification if the visibility actually changed and we are not trying to hide // the wallpaper when we are deferring hiding of the wallpaper. diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 12e5bce0e9fb4..2aeb50b25f418 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -262,7 +262,7 @@ class WindowStateAnimator { mSession = win.mSession; mAttrType = win.mAttrs.type; mIsWallpaper = win.mIsWallpaper; - mWallpaperControllerLocked = mService.mWallpaperControllerLocked; + mWallpaperControllerLocked = mService.mRoot.mWallpaperController; } public void setAnimation(Animation anim, long startTime, int stackClip) { diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 352224036ab23..04e00c4dec0fc 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -96,7 +96,7 @@ class WindowSurfacePlacer { public WindowSurfacePlacer(WindowManagerService service) { mService = service; - mWallpaperControllerLocked = mService.mWallpaperControllerLocked; + mWallpaperControllerLocked = mService.mRoot.mWallpaperController; } /** @@ -383,10 +383,9 @@ class WindowSurfacePlacer { } /** - * @param windows List of windows on default display. * @return bitmap indicating if another pass through layout must be made. */ - int handleAppTransitionReadyLocked(WindowList windows) { + int handleAppTransitionReadyLocked() { int appsCount = mService.mOpeningApps.size(); if (!transitionGoodToGo(appsCount)) { return 0; @@ -429,7 +428,7 @@ class WindowSurfacePlacer { // (like the clearAnimatingFlags() above) might affect wallpaper target result. // Or, the opening app window should be a wallpaper target. mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent, - mService.mOpeningApps, windows); + mService.mOpeningApps); final WindowState lowerWallpaperTarget = mWallpaperControllerLocked.getLowerWallpaperTarget(); @@ -533,7 +532,7 @@ class WindowSurfacePlacer { // TODO: Probably not needed once the window list always has the right z-ordering // when the window hierarchy is updated. final DisplayContent dc = mService.getDefaultDisplayContentLocked(); - if (windows == dc.getWindowList() && !dc.moveInputMethodWindowsIfNeeded(true)) { + if (!dc.moveInputMethodWindowsIfNeeded(true)) { dc.assignWindowLayers(false /*setLayoutNeeded*/); } mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 47366685b44e7..afcdc41321409 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -269,7 +269,7 @@ class WindowToken extends WindowContainer { } void updateWallpaperOffset(int dw, int dh, boolean sync) { - final WallpaperController wallpaperController = mService.mWallpaperControllerLocked; + final WallpaperController wallpaperController = mDisplayContent.mWallpaperController; for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) { final WindowState wallpaper = mChildren.get(wallpaperNdx); if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) { @@ -294,7 +294,7 @@ class WindowToken extends WindowContainer { mDisplayContent.setLayoutNeeded(); } - final WallpaperController wallpaperController = mService.mWallpaperControllerLocked; + final WallpaperController wallpaperController = mDisplayContent.mWallpaperController; for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) { final WindowState wallpaper = mChildren.get(wallpaperNdx); if (visible) { @@ -317,7 +317,7 @@ class WindowToken extends WindowContainer { mDisplayContent.setLayoutNeeded(); } - final WallpaperController wallpaperController = mService.mWallpaperControllerLocked; + final WallpaperController wallpaperController = mDisplayContent.mWallpaperController; for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) { final WindowState wallpaper = mChildren.get(wallpaperNdx); From e6f806e4fa5c73d6cd6334f6aafa4c7688fd3aca Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Thu, 20 Oct 2016 15:29:42 -0700 Subject: [PATCH 2/3] Moved display related getting of orientation into DisplayContent 5th step in trying to make WindowList private to DisplayContent. Exact code to get orientation in WMS that is display specific because it iterates over the window list into the DisplayContent object. Test: Manual testing and exiting test pass. Change-Id: Iced72322cdebdde83882802af4a9decf323f20e1 --- .../com/android/server/wm/DisplayContent.java | 125 ++++++++++++++---- .../server/wm/WindowManagerService.java | 77 +---------- 2 files changed, 103 insertions(+), 99 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index cefa4f932f294..4a7f816226fbc 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -366,33 +366,79 @@ class DisplayContent extends WindowContainer= 0; --pos) { + final WindowState win = mWindows.get(pos); + if (win.mAppToken != null) { + // We hit an application window. so the orientation will be determined by the + // app window. No point in continuing further. + break; + } + if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) { + continue; + } + int req = win.mAttrs.screenOrientation; + if(req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND) { + continue; + } + + if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req); + if (policy.isKeyguardHostWindow(win.mAttrs)) { + mService.mLastKeyguardForcedOrientation = req; + } + return (mService.mLastWindowForcedOrientation = req); + } + mService.mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED; + + if (policy.isKeyguardLocked()) { + // The screen is locked and no top system window is requesting an orientation. + // Return either the orientation of the show-when-locked app (if there is any) or + // the orientation of the keyguard. No point in searching from the rest of apps. + WindowState winShowWhenLocked = (WindowState) policy.getWinShowWhenLockedLw(); + AppWindowToken appShowWhenLocked = winShowWhenLocked == null + ? null : winShowWhenLocked.mAppToken; + if (appShowWhenLocked != null) { + int req = appShowWhenLocked.getOrientation(); + if (req == SCREEN_ORIENTATION_BEHIND) { + req = mService.mLastKeyguardForcedOrientation; + } + if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + appShowWhenLocked + + " -- show when locked, return " + req); + return req; + } + if (DEBUG_ORIENTATION) Slog.v(TAG_WM, + "No one is requesting an orientation when the screen is locked"); + return mService.mLastKeyguardForcedOrientation; } - return SCREEN_ORIENTATION_UNSPECIFIED; } - final int orientation = super.getOrientation(); - if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) { - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, - "App is requesting an orientation, return " + orientation); - return orientation; - } - - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, - "No app is requesting an orientation, return " + mService.mLastOrientation); - // The next app has not been requested to be visible, so we keep the current orientation - // to prevent freezing/unfreezing the display too early. - return mService.mLastOrientation; + // Top system windows are not requesting an orientation. Start searching from apps. + return mTaskStackContainers.getOrientation(); } void updateDisplayInfo() { @@ -2306,6 +2352,37 @@ class DisplayContent extends WindowContainer= 0; --pos) { - WindowState win = windows.get(pos); - if (win.mAppToken != null) { - // We hit an application window. so the orientation will be determined by the - // app window. No point in continuing further. - break; - } - if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) { - continue; - } - int req = win.mAttrs.screenOrientation; - if(req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND) { - continue; - } - - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req); - if (mPolicy.isKeyguardHostWindow(win.mAttrs)) { - mLastKeyguardForcedOrientation = req; - } - return (mLastWindowForcedOrientation = req); - } - mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED; - - if (mPolicy.isKeyguardLocked()) { - // The screen is locked and no top system window is requesting an orientation. - // Return either the orientation of the show-when-locked app (if there is any) or - // the orientation of the keyguard. No point in searching from the rest of apps. - WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw(); - AppWindowToken appShowWhenLocked = winShowWhenLocked == null ? - null : winShowWhenLocked.mAppToken; - if (appShowWhenLocked != null) { - int req = appShowWhenLocked.getOrientation(); - if (req == SCREEN_ORIENTATION_BEHIND) { - req = mLastKeyguardForcedOrientation; - } - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + appShowWhenLocked - + " -- show when locked, return " + req); - return req; - } - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, - "No one is requesting an orientation when the screen is locked"); - return mLastKeyguardForcedOrientation; - } - } - - // Top system windows are not requesting an orientation. Start searching from apps. - return getDefaultDisplayContentLocked().getOrientation(); - } - @Override public Configuration updateOrientationFromAppTokens( Configuration currentConfig, IBinder freezeThisOneIfNeeded) { @@ -2684,7 +2610,8 @@ public class WindowManagerService extends IWindowManager.Stub boolean updateOrientationFromAppTokensLocked(boolean inTransaction) { long ident = Binder.clearCallingIdentity(); try { - int req = getOrientationLocked(); + // TODO: multi-display + int req = getDefaultDisplayContentLocked().getOrientation(); if (req != mLastOrientation) { mLastOrientation = req; //send a message to Policy indicating orientation change to take From 494009b8e3d6c2751d0c6983f24b56971f6bdf9d Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Fri, 21 Oct 2016 09:01:38 -0700 Subject: [PATCH 3/3] Removed WMS.getDefaultWindowListLocked() 6th step in trying to make the WindowList private to DisplayContent. The method was another an indirect way to the the window list from the display content. Test: Manual testing and existing tests pass. Change-Id: I26de316b8c20dd891a214fb40ef3991a0e895442 --- .../com/android/server/wm/DisplayContent.java | 176 ++++++++++++++++ .../server/wm/WindowManagerService.java | 190 ++---------------- 2 files changed, 194 insertions(+), 172 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4a7f816226fbc..10fecf44e8204 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -36,12 +36,17 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; @@ -55,6 +60,7 @@ import static com.android.server.wm.WindowAnimator.KEYGUARD_NOT_SHOWN; import static com.android.server.wm.WindowAnimator.KEYGUARD_SHOWN; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; @@ -63,6 +69,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; @@ -71,9 +78,11 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT; +import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT; import static com.android.server.wm.WindowManagerService.dipToPixel; import static com.android.server.wm.WindowManagerService.localLOGV; import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; +import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED; @@ -91,6 +100,8 @@ import android.hardware.display.DisplayManagerInternal; import android.os.Debug; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemClock; import android.util.DisplayMetrics; import android.util.Slog; import android.view.Display; @@ -102,6 +113,7 @@ import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import com.android.internal.util.FastPrintWriter; +import com.android.internal.view.IInputMethodClient; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -1897,6 +1909,60 @@ class DisplayContent extends WindowContainer= 0; --i) { + final WindowState ws = mWindows.get(i); + if (ws.isOnScreen() && (ws.mAttrs.flags & FLAG_SECURE) != 0) { + return true; + } + } + return false; + } + + void updateSystemUiVisibility(int visibility, int globalDiff) { + for (int i = mWindows.size() - 1; i >= 0; --i) { + final WindowState ws = mWindows.get(i); + try { + int curValue = ws.mSystemUiVisibility; + int diff = (curValue ^ visibility) & globalDiff; + int newValue = (curValue & ~diff) | (visibility & diff); + if (newValue != curValue) { + ws.mSeq++; + ws.mSystemUiVisibility = newValue; + } + if (newValue != curValue || ws.mAttrs.hasSystemUiListeners) { + ws.mClient.dispatchSystemUiVisibilityChanged(ws.mSeq, + visibility, newValue, diff); + } + } catch (RemoteException e) { + // so sorry + } + } + } + + void onWindowFreezeTimeout() { + Slog.w(TAG_WM, "Window freeze timeout expired."); + mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; + for (int i = mWindows.size() - 1; i >= 0; --i) { + final WindowState w = mWindows.get(i); + if (!w.mOrientationChanging) { + continue; + } + w.mOrientationChanging = false; + w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() + - mService.mDisplayFreezeTime); + Slog.w(TAG_WM, "Force clearing orientation change: " + w); + } + mService.mWindowPlacerLocked.performSurfacePlacement(); + } + + void waitForAllWindowsDrawn() { + final WindowManagerPolicy policy = mService.mPolicy; + for (int winNdx = mWindows.size() - 1; winNdx >= 0; --winNdx) { + final WindowState win = mWindows.get(winNdx); + final boolean isForceHiding = policy.isForceHiding(win.mAttrs); + final boolean keyguard = policy.isKeyguardHostWindow(win.mAttrs); + if (win.isVisibleLw() && (win.mAppToken != null || isForceHiding || keyguard)) { + win.mWinAnimator.mDrawState = DRAW_PENDING; + // Force add to mResizingWindows. + win.mLastContentInsets.set(-1, -1, -1, -1); + mService.mWaitingForDrawn.add(win); + + // No need to wait for the windows below Keyguard. + if (isForceHiding) { + return; + } + } + } + } + static final class GetWindowOnDisplaySearchResult { boolean reachedToken; WindowState foundWindow; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index bdd7e568f8a40..40001b22beb0b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -190,6 +190,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.LayoutParams.TYPE_DRAG; import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -515,9 +516,9 @@ public class WindowManagerService extends IWindowManager.Stub // The root of the device window hierarchy. RootWindowContainer mRoot; - // TODO: Move several of this states to the RootWindowContainer + // TODO: Move several of this states to the RootWindowContainer or DisplayContent int mRotation = 0; - int mLastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED; boolean mAltOrientation = false; private boolean mKeyguardWaitingForActivityDrawn; @@ -530,8 +531,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mForceResizableTasks = false; int getDragLayerLocked() { - return mPolicy.windowTypeToLayerLw(LayoutParams.TYPE_DRAG) * TYPE_LAYER_MULTIPLIER - + TYPE_LAYER_OFFSET; + return mPolicy.windowTypeToLayerLw(TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; } class RotationWatcher { @@ -562,8 +562,8 @@ public class WindowManagerService extends IWindowManager.Stub boolean mClientFreezingScreen = false; int mAppsFreezingScreen = 0; - int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - int mLastKeyguardForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + int mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED; + int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED; int mLayoutSeq = 0; @@ -4142,64 +4142,7 @@ public class WindowManagerService extends IWindowManager.Stub performEnableScreen(); } - private boolean checkWaitingForWindowsLocked() { - - boolean haveBootMsg = false; - boolean haveApp = false; - // if the wallpaper service is disabled on the device, we're never going to have - // wallpaper, don't bother waiting for it - boolean haveWallpaper = false; - boolean wallpaperEnabled = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_enableWallpaperService) - && !mOnlyCore; - boolean haveKeyguard = true; - // TODO(multidisplay): Expand to all displays? - final WindowList windows = getDefaultWindowListLocked(); - final int N = windows.size(); - for (int i=0; i 0) { - i--; - WindowState w = windows.get(i); - if (w.mOrientationChanging) { - w.mOrientationChanging = false; - w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() - - mDisplayFreezeTime); - Slog.w(TAG_WM, "Force clearing orientation change: " + w); - } - } - mWindowPlacerLocked.performSurfacePlacement(); + getDefaultDisplayContentLocked().onWindowFreezeTimeout(); } break; } @@ -7075,45 +7006,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean inputMethodClientHasFocus(IInputMethodClient client) { synchronized (mWindowMap) { - // The focus for the client is the window immediately below - // where we would place the input method window. // TODO: multi-display - int idx = getDefaultDisplayContentLocked().findDesiredInputMethodWindowIndex(false); - if (idx > 0) { - // TODO(multidisplay): IMEs are only supported on the default display. - WindowState imFocus = getDefaultWindowListLocked().get(idx-1); - if (DEBUG_INPUT_METHOD) { - Slog.i(TAG_WM, "Desired input method target: " + imFocus); - Slog.i(TAG_WM, "Current focus: " + mCurrentFocus); - Slog.i(TAG_WM, "Last focus: " + mLastFocus); - } - if (imFocus != null) { - // This may be a starting window, in which case we still want - // to count it as okay. - if (imFocus.mAttrs.type == TYPE_APPLICATION_STARTING - && imFocus.mAppToken != null) { - // The client has definitely started, so it really should - // have a window in this app token. Let's look for it. - final WindowState w = imFocus.mAppToken.getFirstNonStartingWindow(); - if (w != null) { - if (DEBUG_INPUT_METHOD) Slog.i(TAG_WM, - "Switching to real app window: " + w); - imFocus = w; - } - } - if (DEBUG_INPUT_METHOD) { - Slog.i(TAG_WM, "IM target client: " + imFocus.mSession.mClient); - if (imFocus.mSession.mClient != null) { - Slog.i(TAG_WM, "IM target client binder: " - + imFocus.mSession.mClient.asBinder()); - Slog.i(TAG_WM, "Requesting client binder: " + client.asBinder()); - } - } - if (imFocus.mSession.mClient != null && - imFocus.mSession.mClient.asBinder() == client.asBinder()) { - return true; - } - } + if (getDefaultDisplayContentLocked().inputMethodClientHasFocus(client)) { + return true; } // Okay, how about this... what is the current focus? @@ -7762,16 +7657,7 @@ public class WindowManagerService extends IWindowManager.Stub } // Check whether the current screen contains any secure content. - boolean isSecure = false; - final WindowList windows = getDefaultWindowListLocked(); - final int N = windows.size(); - for (int i = 0; i < N; i++) { - WindowState ws = windows.get(i); - if (ws.isOnScreen() && (ws.mAttrs.flags & FLAG_SECURE) != 0) { - isSecure = true; - break; - } - } + boolean isSecure = displayContent.hasSecureWindowOnScreen(); // TODO(multidisplay): rotation on main screen only. displayContent.updateDisplayInfo(); @@ -7977,8 +7863,8 @@ public class WindowManagerService extends IWindowManager.Stub } } - // TOOD(multidisplay): StatusBar on multiple screens? - boolean updateStatusBarVisibilityLocked(int visibility) { + // TODO(multidisplay): StatusBar on multiple screens? + private boolean updateStatusBarVisibilityLocked(int visibility) { if (mLastDispatchedSystemUiVisibility == visibility) { return false; } @@ -7991,26 +7877,7 @@ public class WindowManagerService extends IWindowManager.Stub mLastDispatchedSystemUiVisibility = visibility; mInputManager.setSystemUiVisibility(visibility); - final WindowList windows = getDefaultWindowListLocked(); - final int N = windows.size(); - for (int i = 0; i < N; i++) { - WindowState ws = windows.get(i); - try { - int curValue = ws.mSystemUiVisibility; - int diff = (curValue ^ visibility) & globalDiff; - int newValue = (curValue&~diff) | (visibility&diff); - if (newValue != curValue) { - ws.mSeq++; - ws.mSystemUiVisibility = newValue; - } - if (newValue != curValue || ws.mAttrs.hasSystemUiListeners) { - ws.mClient.dispatchSystemUiVisibilityChanged(ws.mSeq, - visibility, newValue, diff); - } - } catch (RemoteException e) { - // so sorry - } - } + getDefaultDisplayContentLocked().updateSystemUiVisibility(visibility, globalDiff); return true; } @@ -8615,10 +8482,6 @@ public class WindowManagerService extends IWindowManager.Stub return mRoot.getDisplayContentOrCreate(DEFAULT_DISPLAY); } - WindowList getDefaultWindowListLocked() { - return getDefaultDisplayContentLocked().getWindowList(); - } - private DisplayInfo getDefaultDisplayInfoLocked() { return getDefaultDisplayContentLocked().getDisplayInfo(); } @@ -9152,24 +9015,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean allWindowsDrawn = false; synchronized (mWindowMap) { mWaitingForDrawnCallback = callback; - final WindowList windows = getDefaultWindowListLocked(); - for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { - final WindowState win = windows.get(winNdx); - final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs); - final boolean keyguard = mPolicy.isKeyguardHostWindow(win.mAttrs); - if (win.isVisibleLw() - && (win.mAppToken != null || isForceHiding || keyguard)) { - win.mWinAnimator.mDrawState = DRAW_PENDING; - // Force add to mResizingWindows. - win.mLastContentInsets.set(-1, -1, -1, -1); - mWaitingForDrawn.add(win); - - // No need to wait for the windows below Keyguard. - if (isForceHiding) { - break; - } - } - } + getDefaultDisplayContentLocked().waitForAllWindowsDrawn(); mWindowPlacerLocked.requestTraversal(); mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT); if (mWaitingForDrawn.isEmpty()) {