Waiting for drawn Window耗時過長流程梳理

 存在一個問題:負載指紋響應時間衰退達到1秒多,從log中看Waiting for drawn Window 耗時太長

Line 3204: 08-14 14:44:45.929433  1100  5600 I PowerManagerService: Waking up from sleep (uid=10028 reason=android.policy:FINGERPRINT)...
Line 3211: 08-14 14:44:45.939175  1100  1135 I DisplayPowerController: Blocking screen on until initial contents have been drawn.
///Launcher 處於DRAW_PENDING 狀態,這是一個surface已經創建但是Window還沒draw的狀態
Line 3250: 08-14 14:44:46.462380  1100  1121 I WindowManager: Waiting for drawn Window{f9de4b3 u0  com.android.launcher3/com.android.launcher3.Launcher}: removed=false visible=true mHasSurface=true drawState=1
Line 3251: 08-14 14:44:46.462434  1100  1121 I WindowManager: Waiting for drawn Window{18b56d9 u0 StatusBar}: removed=false visible=true mHasSurface=true drawState=1
///StatusBar HAS_DRAWN 完成繪製
Line 3276: 08-14 14:44:46.757318  1100  1176 I WindowManager: Waiting for drawn Window{18b56d9 u0 StatusBar}: removed=false visible=true mHasSurface=true drawState=4
    ....   
///Launcher 繪製完成持續時間比較久,從14:44:46.462380持續到14:44:47.221558,耗時接近800毫秒
Line 3298: 08-14 14:44:47.221558  1100  1176 I WindowManager: Waiting for drawn Window{f9de4b3 u0  com.android.launcher3/com.android.launcher3.Launcher}: removed=false visible=true mHasSurface=true drawState=4
     08-14 14:44:47.221628  1100  1176 D WindowManager: Window drawn win=Window{f9de4b3 u0 com.android.launcher3/com.android.launcher3.Launcher}
     08-14 14:44:47.221644  1100  1176 D WindowManager: All windows drawn!
     08-14 14:44:47.252180  1100  1135 I DisplayPowerController: Unblocked screen on after 1313 ms
     ///這一次power screen on 耗時1.33S
     08-14 14:44:47.257309  1100  1135 W PowerManagerService: Screen on took 1330 ms

 如上log中drawstate有五個狀態,一般是從DRAW_PENDING 開始等待到HAS_DRAWN這一段耗時太長。

/frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
    /** This is set when there is no Surface */
    static final int NO_SURFACE = 0;
    /** This is set after the Surface has been created but before the window has been drawn. During
     * this time the surface is hidden. */
    static final int DRAW_PENDING = 1;
    /** This is set after the window has finished drawing for the first time but before its surface
     * is shown.  The surface will be displayed when the next layout is run. */
    static final int COMMIT_DRAW_PENDING = 2;
    /** This is set during the time after the window's drawing has been committed, and before its
     * surface is actually shown.  It is used to delay showing the surface until all windows in a
     * token are ready to be shown. */
    static final int READY_TO_SHOW = 3;
    /** Set when the window has been shown in the screen the first time. */
    static final int HAS_DRAWN = 4;

往下再看一下DRAW_PENDING和HAS_DRAWN分別是在哪邊賦值的。

/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    void waitForAllWindowsDrawn() {
        final WindowManagerPolicy policy = mService.mPolicy;
        forAllWindows(w -> {
            final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs);
            if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
                w.mWinAnimator.mDrawState = DRAW_PENDING;
                // Force add to mResizingWindows.
                w.mLastContentInsets.set(-1, -1, -1, -1);
                mService.mWaitingForDrawn.add(w);
            }
        }, true /* traverseTopToBottom */);
    }

三個調用的點

1.MSG_KEYGUARD_DRAWN_COMPLETE,

2.MSG_KEYGUARD_DRAWN_TIMEOUT,

3.screenTurningOn

----->PhoneWindowManger.finishKeyguardDrawn----->WindowManagerService.waitForAllWindowsDrawn---->DisplayContent.waitForAllWindowsDrawn

在WindowManagerService的waitForAllWindowsDrawn中

        @Override
        public void waitForAllWindowsDrawn(Runnable callback, long timeout) {
            boolean allWindowsDrawn = false;
            synchronized (mWindowMap) {
                mWaitingForDrawnCallback = callback;
                 //即對應的DisplayContent的waitForAllWindowsDrawn
                getDefaultDisplayContentLocked().waitForAllWindowsDrawn();
                mWindowPlacerLocked.requestTraversal();
                mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
                if (mWaitingForDrawn.isEmpty()) {
                    allWindowsDrawn = true;
                } else {
                    mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout);
                    checkDrawnWindowsLocked();
                }
            }
            if (allWindowsDrawn) {
                callback.run();
            }
        }

其中等待繪製的界面不爲空走checkDrawnWindowsLocked

    void checkDrawnWindowsLocked() {
        if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
            return;
        }
        for (int j = mWaitingForDrawn.size() - 1; j >= 0; j--) {
            WindowState win = mWaitingForDrawn.get(j);
            if (DEBUG_SCREEN_ON) Slog.i(TAG_WM, "Waiting for drawn " + win +
                    ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +
                    " mHasSurface=" + win.mHasSurface +
                    " drawState=" + win.mWinAnimator.mDrawState);
            if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) {
                // Window has been removed or hidden; no draw will now happen, so stop waiting.
                if (DEBUG_SCREEN_ON) Slog.w(TAG_WM, "Aborted waiting for drawn: " + win);
                mWaitingForDrawn.remove(win);
            } else if (win.hasDrawnLw()) {//即等於HAS_DRAWN狀態會將win刪除
                // Window is now drawn (and shown).
                if (DEBUG_SCREEN_ON) Slog.d(TAG_WM, "Window drawn win=" + win);
                mWaitingForDrawn.remove(win);
            }
        }
        if (mWaitingForDrawn.isEmpty()) {
            if (DEBUG_SCREEN_ON) Slog.d(TAG_WM, "All windows drawn!");
            mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
            mH.sendEmptyMessage(H.ALL_WINDOWS_DRAWN);
        }
    }

而HAS_DRAWN在frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    // This must be called while inside a transaction.
    boolean performShowLocked() {
        if (isHiddenFromUserLocked()) {
            if (DEBUG_VISIBILITY) Slog.w(TAG, "hiding " + this + ", belonging to " + mOwnerUid);
            hideLw(false);
            return false;
        }

        logPerformShow("performShow on ");

        final int drawState = mWinAnimator.mDrawState;
        if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW)
                && mAttrs.type != TYPE_APPLICATION_STARTING && mAppToken != null) {
            mAppToken.onFirstWindowDrawn(this, mWinAnimator);
        }

        if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
            return false;
        }

        logPerformShow("Showing ");

        mService.enableScreenIfNeededLocked();
        mWinAnimator.applyEnterAnimationLocked();

        // Force the show in the next prepareSurfaceLocked() call.
        mWinAnimator.mLastAlpha = -1;
        if (DEBUG_ANIM) Slog.v(TAG,
                "performShowLocked: mDrawState=HAS_DRAWN in " + this);
        mWinAnimator.mDrawState = HAS_DRAWN;
        mService.scheduleAnimationLocked();

        if (mHidden) {
            mHidden = false;
            final DisplayContent displayContent = getDisplayContent();

            for (int i = mChildren.size() - 1; i >= 0; --i) {
                final WindowState c = mChildren.get(i);
                if (c.mWinAnimator.mSurfaceController != null) {
                    c.performShowLocked();
                    // It hadn't been shown, which means layout not performed on it, so now we
                    // want to make sure to do a layout.  If called from within the transaction
                    // loop, this will cause it to restart with a new layout.
                    if (displayContent != null) {
                        displayContent.setLayoutNeeded();
                    }
                }
            }
        }

        if (mAttrs.type == TYPE_INPUT_METHOD) {
            getDisplayContent().mDividerControllerLocked.resetImeHideRequested();
        }

        return true;
    }

WindowAnimator.animate----->WindowContainer.checkAppWindowsReadyToShow--->AppWindowToken.checkAppWindowsReadyToShow--->AppWindowToken.showAllWindowsLocked---->WindowState.performShowLocked

    /** Checks if all windows in an app are all drawn and shows them if needed. */
    void checkAppWindowsReadyToShow() {
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowContainer wc = mChildren.get(i);
            wc.checkAppWindowsReadyToShow();
        }
    }
    /**
     * 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) {
....
        synchronized (mService.mWindowMap) {
             if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animate");
             mService.openSurfaceTransaction();
.....
                final int numDisplays = mDisplayContentsAnimators.size();
                for (int i = 0; i < numDisplays; i++) {
                    final int displayId = mDisplayContentsAnimators.keyAt(i);
                    final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
                    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.prepareSurfaces();
                }
....
                for (int i = 0; i < numDisplays; i++) {
                    final int displayId = mDisplayContentsAnimators.keyAt(i);
                    final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);

                    dc.checkAppWindowsReadyToShow();

                    final ScreenRotationAnimation screenRotationAnimation =
                            mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
                    if (screenRotationAnimation != null) {
                        screenRotationAnimation.updateSurfaces(mTransaction);
                    }
                    orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
                    //TODO (multidisplay): Magnification is supported only for the default display.
                    if (accessibilityController != null && dc.isDefaultDisplay) {
                        accessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
                    }
                }
                if (!mAnimating) {
                    cancelAnimation();
                }

                if (mService.mWatermark != null) {
                    mService.mWatermark.drawIfNeeded();
                }

                SurfaceControl.mergeToGlobalTransaction(mTransaction);
           }finally {
                  mService.closeSurfaceTransaction("WindowAnimator");
                  if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
              }
....
}
....

   }

一個有鎖屏界面按Power鍵亮屏的界面繪製流程log 

08-21 03:04:57.616 I/PowerManagerService( 1025): Waking up from sleep (uid=1000 reason=android.policy:POWER)...
08-21 03:04:57.619 I/DisplayPowerController( 1025): Blocking screen on until initial contents have been drawn.
08-21 03:04:57.703 I/WindowManager( 1025): Waiting for drawn Window{94a4fbd u0 StatusBar}: removed=false visible=true mHasSurface=true drawState=1
....
08-21 03:04:57.736 I/WindowManager( 1025): Waiting for drawn Window{94a4fbd u0 StatusBar}: removed=false visible=true mHasSurface=true drawState=1
08-21 03:04:57.746 D/WindowManager( 1025): finishDrawingWindow: Window{c72bd2b u0 SmartPanel_SliderView} mDrawState=HAS_DRAWN
08-21 03:04:57.765 D/WindowManager( 1025): finishDrawingWindow: Window{94a4fbd u0 StatusBar} mDrawState=DRAW_PENDING
08-21 03:04:57.766 W/WindowManager( 1025): setLayoutNeeded: callers=com.android.server.wm.WindowState.setDisplayLayoutNeeded:2312 com.android.server.wm.WindowManagerService.finishDrawingWindow:2441 com.android.server.wm.Session.finishDrawing:282 
08-21 03:04:57.766 V/WindowManager( 1025): performSurfacePlacementInner: entry. Called by com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop:208 com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement:156 com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement:146 
08-21 03:04:57.766 I/WindowManager( 1025): >>> OPEN TRANSACTION performLayoutAndPlaceSurfaces
....
08-21 03:04:57.772 I/WindowManager( 1025): commitFinishDrawingLocked: mDrawState=READY_TO_SHOW Surface(name=StatusBar)/@0x6490d9c
08-21 03:04:57.772 V/WindowManager( 1025): performShow on Window{94a4fbd u0 StatusBar}: mDrawState=READY_TO_SHOW readyForDisplay=true starting=false during animation: policyVis=true parentHidden=false tok.hiddenRequested=false tok.hidden=false animationSet=false tok animating=false Callers=com.android.server.wm.WindowState.performShowLocked:3891
....
08-21 03:04:57.773 V/WindowManager( 1025): applyAnimation: win=WindowStateAnimator{f7df172 StatusBar} anim=-1 attr=0xffffffff a=null transit=3 isEntrance=true Callers 
08-21 03:04:57.773 V/WindowManager( 1025): performShowLocked: mDrawState=HAS_DRAWN in Window{94a4fbd u0 StatusBar}
...
08-21 03:04:57.776 I/WindowManager( 1025): <<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces
...
08-21 03:04:57.777 I/WindowManager( 1025): Waiting for drawn Window{94a4fbd u0 StatusBar}: removed=false visible=true mHasSurface=true drawState=4
08-21 03:04:57.885 W/PowerManagerService( 1025): Screen on took 278 ms

 

ViewRootImpl.performDraw--->pendingDrawFinished---->reportDrawFinished--->mWindowSession.finishDrawing(mWindow)--->
Session.finishDrawing---->WindowManagerService.finishDrawingWindow--->

    void finishDrawingWindow(Session session, IWindow client) {
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mWindowMap) {
                WindowState win = windowForClientLocked(session, client, false);
                if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "finishDrawingWindow: " + win + " mDrawState="
                        + (win != null ? win.mWinAnimator.drawStateToString() : "null"));
                if (win != null && win.mWinAnimator.finishDrawingLocked()) {
                    if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                        win.getDisplayContent().pendingLayoutChanges |=
                                WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                    }
                    win.setDisplayLayoutNeeded();
                    mWindowPlacerLocked.requestTraversal();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章