Android P WMS -- windowanimator

Android P WMS (1) -- Introduction to WMS

Android P WMS(2) -- wms initialization

Android O WMS(3) -- addwindow

Android P WMS(4) -- removewindow

Android P WMS(5) -- relayoutWindow

Android P WMS(6) -- windowanimator

Android P WMS(7) --wms Problem Types and Deug Skills

Android P WMS (8) -- Introduction to View SYstem

Android P WMS(9) --Surface

1. Overview of Animation

In Android system, the essence of window animation is to apply a Transformation to the original window. In linear mathematics, the shape of an object is transformed by multiplying a matrix (Matrix). The purpose is to offset, rotate, zoom, shear, reflection and projection of the object. Therefore, animating a window actually sets a Transformation Matrix for the window.

As mentioned earlier, a window in the process of opening (closing) may be set up three animations, which are the self-transformation set by the window itself, the Attached Transformation passed from the attached window, and the host Activity component. The handover animation (App Transformation) passed in. These three transformations are combined to form a transformation matrix, which is applied to the original shape of the window at the speed of 60 FPs to complete the animation process of the window.

The transformation matrix of a window is applied to the original position and size of the window. Therefore, before animating the window, besides setting the transformation matrix for the window, it is necessary to calculate the original position and size of the window, as well as the layout and the UI of the window. We have analyzed the process of calculating the location and size of windows and the layout and drawing process of window UI in previous blogs. This paper mainly focuses on the process of setting, synthesizing and displaying window animation. The three processes are described in the following four parts:
Setting process of window animation
2. Display framework of window animation
3. Progressive process of window animation
4. Composition process of window animation
The setting process of window animation includes the setting process of the three animations mentioned above. The advancing process of window animation refers to the step-by-step migration process of specified animation. The synthesis process of window animation refers to the process of combining the three animations into a transformation matrix, and the latter two processes include window animation. In the display frame.
 

Window s Animation
Including dialog display hiding, toast display hiding, system window (keyguard navigation bar wallpaper startwindow) entry or exit
Transition Animation Control
Including screen animation, activity switching, fragment switching
View Animation
Key Class Correspondence
AppWindows Animator. Java Settings to Manage Transition Animation
Windows StateAnimator. Java manages window state and window animation
ScreRotation Animation. Java Rotation Animation
Windows Animator. Java Animation Package, wms has only one object
Key processes
loadAnimation  setAnimation  stepAnimationLocked

 

2. Loading of ApplyEnter Animation Locked Animation Resources


1) Window-related animation Windows State Animator, when an Activity starts, it calls relayout Windows of WMS to apply for the window, which is applied to the switching animation of the window. If the window enters the animation, it calls the function applyEnterAnimation Locked in Windows State Animator. java.

/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
void performSurfacePlacement(boolean recoveringMemory) {
    if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
            + Debug.getCallers(3));


    if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
            ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
    mService.openSurfaceTransaction();      //1.1
    try {
        applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh);  //1.2
    } catch (RuntimeException e) {
        Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
    } finally {
        mService.closeSurfaceTransaction();     //1.3
        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
    }

    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();   //1.4

    }

    if (!mService.mAnimator.mAppWindowAnimating && mService.mAppTransition.isRunning()) {
        // We have finished the animation of an app transition. To do this, we have delayed a
        // lot of operations like showing and hiding apps, moving apps in Z-order, etc. The app
        // token list reflects the correct Z-order, but the window list may now be out of sync
        // with it. So here we will just rebuild the entire app window list. Fun!
        defaultDisplay.pendingLayoutChanges |=
                mService.handleAnimatingStoppedAndTransitionLocked();
        if (DEBUG_LAYOUT_REPEATS)
            surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock",
                    defaultDisplay.pendingLayoutChanges);
    }

    
    // Check to see if we are now in a state where the screen should
    // be enabled, because the window obscured flags have changed.
    mService.enableScreenIfNeededLocked();

    mService.scheduleAnimationLocked(); //1.5
    mService.mWindowPlacerLocked.destroyPendingSurfaces();

    if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
            "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
}



 /frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
    int handleAppTransitionReadyLocked() {
        int appsCount = mService.mOpeningApps.size();
        if (!transitionGoodToGo(appsCount)) {   //1.4.1  transitionGoodToGo
            return 0;
        }
        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");

        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
        int transit = mService.mAppTransition.getAppTransition();
        if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
            transit = AppTransition.TRANSIT_UNSET;
        }
        mService.mSkipAppTransitionAnimation = false;
        mService.mNoAnimationNotifyOnTransitionFinished.clear();

        mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);  //1.4.2

        
        processApplicationsAnimatingInPlace(transit);

        mTmpLayerAndToken.token = null;
        handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);  //1.4.3
        final AppWindowToken topClosingApp = mTmpLayerAndToken.token;
        final int topClosingLayer = mTmpLayerAndToken.layer;

        final AppWindowToken topOpeningApp = handleOpeningApps(transit,  //1.4.4
                animLp, voiceInteraction, topClosingLayer);

        mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);


    }

/frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java 
private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
        boolean voiceInteraction, int topClosingLayer) {
    AppWindowToken topOpeningApp = null;
    final int appsCount = mService.mOpeningApps.size();
    for (int i = 0; i < appsCount; i++) {
  

        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                ">>> OPEN TRANSACTION handleAppTransitionReadyLocked()");
        mService.openSurfaceTransaction();
        try {
            mService.mAnimator.orAnimating(appAnimator.showAllWindowsLocked());  //1.4.4.1
        } finally {
            mService.closeSurfaceTransaction();
            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                    "<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()");
        }
       ...
    return topOpeningApp;
}
    
    
/frameworks/base/services/core/java/com/android/server/wm/AppWindowAnimator.java

// This must be called while inside a transaction.
boolean showAllWindowsLocked() {
    boolean isAnimating = false;
    final int NW = mAllAppWinAnimators.size();
    for (int i=0; i<NW; i++) {
        WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
        if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + winAnimator);
        winAnimator.mWin.performShowLocked();  //1.4.4.1.1
        isAnimating |= winAnimator.isAnimationSet();
    }
    return isAnimating;
}
    
@/frameworks/base/services/core/java/com/android/server/wm/WindowState.java 
boolean performShowLocked() {
    ...
    logPerformShow("Showing ");

    mService.enableScreenIfNeededLocked();
    mWinAnimator.applyEnterAnimationLocked();   //1. Loading of Animation Resources

    // Force the show in the next prepareSurfaceLocked() call.
    mWinAnimator.mLastAlpha = -1;
    if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) Slog.v(TAG,
            "performShowLocked: mDrawState=HAS_DRAWN in " + this);
    mWinAnimator.mDrawState = HAS_DRAWN;
    mService.scheduleAnimationLocked();   //2 Many places, such as hideLW, have scheduled Animation Locked

  

    return true;
}

 

 

Windows State Animator handles not only surface-related operations, but also the tracking of animation processes.

void applyEnterAnimationLocked() @WindowStateAnimator.java{
    final int transit;
    if (mEnterAnimationPending) {
        mEnterAnimationPending = false;
        transit = WindowManagerPolicy.TRANSIT_ENTER;
    }else{
        transit = WindowManagerPolicy.TRANSIT_SHOW;
    }
    applyAnimationLocked(transit, true);
}


From the above functions, entry animation is divided into TRANSIT_ENTER and TRANSIT_SHOW. When mEnter Animation Pending is true, the program executes TRANSIT_ENTER animation. The value of mEnter Animation Pending is set in WMS. One case is when the window addWindow is added: winAnimator. mEnter Animation Pending = true; the other case is when relayout Visible Window, the visible state is from GONE to VISIBLE:

if (oldVisibility ==View.GONE)winAnimator.mEnterAnimationPending = true;

In addition to TRANSIT_ENTER and TRANSIT_SHOW, the types of animation related to window s include:

WindowManagerPolicy.java
    /** Window has been added to the screen. */
    public static final int TRANSIT_ENTER = 1;
    /** Window has been removed from the screen. */
    public static final int TRANSIT_EXIT = 2;
    /** Window has been made visible. */
    public static final int TRANSIT_SHOW = 3;
    /** Window has been made invisible.
     * TODO: Consider removal as this is unused. */
    public static final int TRANSIT_HIDE = 4;
    /** The "application starting" preview window is no longer needed, and will
     * animate away to show the real window. */
    public static final int TRANSIT_PREVIEW_DONE = 5;

Then look at the difference between the applyAnimationLocked function in dealing with TRANSIT_ENTER and TRANSIT_SHOW:

boolean applyAnimationLocked(int transit, boolean isEntrance) @WindowStateAnimator.java{
//If the animation currently being executed is of the same type as the entry animation, then the system does not repeat the execution of the animation.
    if ((mLocalAnimating && mAnimationIsEntrance == isEntrance)|| mKeyguardGoingAwayAnimation) {
        if (mAnimation != null && mKeyguardGoingAwayAnimation
            && transit == WindowManagerPolicy.TRANSIT_PREVIEW_DONE) {
//If tranit is TRANSIT_PREVIEW_DONE and the application window has been drawn, then the animation type will be app_start_exit.
            applyFadeoutDuringKeyguardExitAnimation();
        }
        return true;
    }
//The current screen is in a display state.
    if (mService.okToDisplay()) {
//Special windows such as status bar and navigation bar select matching animation resources through phoneWindows Manager. java. This function directly returns the ID of animation resources such as R.anim.dock_top_enter.
        int anim = mPolicy.selectAnimationLw(mWin, transit);
        int attr = -1;
        Animation a = null;
        if (anim != 0) {
//Load the animation resource of the specified animation resource ID.
            a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;
        }else{
//According to the transit type, get the corresponding attribute ID, such as: com. android. internal. R. styleable. Windows Animation_windowEnterAnimation is the corresponding attribute ID of TRANSIT_ENTER.
            switch (transit) {
                case WindowManagerPolicy.TRANSIT_ENTER:
                attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
                break;
                            case WindowManagerPolicy.TRANSIT_EXIT:
                                attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
                                break;
                            case WindowManagerPolicy.TRANSIT_SHOW:
                               attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
                                break;
            }
//Load the animation resources, first have the attribute value attr, get the corresponding animation id, and then load the specified resources.
             a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr);
        }
//Set up the animation and record the animation resource a into the variable mAnimation in Windows StateAnimator. java.
        setAnimation(a);
    }else{
//If the current screen is not visible, clear the animation.
        clearAnimation();
    }
}


/ This function can dynamically obtain the animation resource ID based on the attribute id, which means that the animation resource can be customized through these attributes.

Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) @AppTransition.java{
        int anim = 0;
        Context context = mContext;
        if (animAttr >= 0) {
            AttributeCache.Entry ent = getCachedAnimations(lp);
            if (ent != null) {
                context = ent.context;
//Get the animation id.
                anim = ent.array.getResourceId(animAttr, 0);
            }
        }
        if (anim != 0) {
//Load animation resources.
            return AnimationUtils.loadAnimation(context, anim);
        }
        return null;
    }

 

3. Schedule Animation Locked Starts Animation

The front is just the loading process of the animation resources. Now let's see how the animation is executed.

In the previous analysis of window application process, we analyzed the call to performance Surface Placement in relayout Windows. At the end of this function, we called mService. scheduleAnimation Locked () to arrange the execution of animation.

void scheduleAnimationLocked() @WindowManagerService.java{
        if (!mAnimationScheduled) {
            mAnimationScheduled = true;
//Setting a trigger source through Choreographer, mChoreographer is saved through ThreadLocal, so it is a single instance in a thread.
            mChoreographer.postFrameCallback(mAnimator.mAnimationFrameCallback);
        }
    }    

The mAnimation Scheduled is true, indicating that the current scheduleanimantion is in progress, and the mAnimation Scheduled will be set to false again in the callback of the mAnimation Frame Callback.

What is the trigger of the animation set by Choreographer? Look at the main function of the Choreographer class: Choreographer receives timing pulses (such as Vsync) from the display system, and then arranges the rendering of the next display frame. The trigger of animation is Vsync.

In animation frameworks or view levels, applications often use higher abstractions to interact indirectly with Choreography, such as using the Android. animation. Value Animator # start method, post ing an animation to synchronize with the rendering frame.

Using the View postOnAnimation method, post a Runnable so that it is called at the beginning of the next display frame.

In some cases, choreography functions may need to be used directly, such as:

If the application wants to render in different threads, it may use GL, or it may not use animation framework, View level, and ensure synchronization with the display, then it may use Choreographer to render # postFrameCallback.

Choreography receives VSYNC signals on the one hand, and forwards this event to interested persons. All objects wishing to listen to VSYNC signals are registered in Choreography.

The postFrameCallback is called in the scheduleAnimation Locked, and then the postFrameCallback Delayed is called directly.

public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) @Choreographer.java{
        postCallbackDelayedInternal(CALLBACK_ANIMATION,
                callback, FRAME_CALLBACK_TOKEN, delayMillis);
    }

Here, CALLBACK_ANIMATION is the callback type. Choreography divides registrants into three types: CALLBACK_INPUT, CALLBACK_ANIMATION, CALLBACK_TRAVERSAL (processing layout and draw).

Registered callbacks are placed in an array mCallbackQueues.

When the Vsync signal is generated, Choreography calls the doFrame function of the corresponding registered object through doCallbacks.

The callback here is the doFrame in Windows Animator. java.

WindowAnimator.java
        mAnimationFrameCallback = new Choreographer.FrameCallback() {
            public void doFrame(long frameTimeNs) {
                synchronized (mService.mWindowMap) {
                    mService.mAnimationScheduled = false;
                    animateLocked(frameTimeNs);
                }
            }
        };


Through animateLocked, various animations begin to be executed step by step.

private void animateLocked(long frameTimeNs) {
//Current time.
    mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
//Record the state of the last animation to determine whether the animation is over.
    boolean wasAnimating = mAnimating;
//First, the changes to surface are recorded locally, and finally unified statistics are given to surfaceflinger.
    SurfaceControl.openTransaction();
    SurfaceControl.setAnimationTransaction();
 
    try {
        final int numDisplays = mDisplayContentsAnimators.size();
//Cyclic processing of animations in each display.
        for (int i = 0; i < numDisplays; i++) {
            final int displayId = mDisplayContentsAnimators.keyAt(i);
//Execute the appWindow s animation set up in appTransition.
            updateAppWindowsLocked(displayId);
//Perform screen rotation animations, such as horizontal and vertical screens.
            final ScreenRotationAnimation screenRotationAnimation 
                =displayAnimator.mScreenRotationAnimation;
            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
                if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
//The animation is not finished, set mAnimating to true through setAnimating.
                    setAnimating(true);
                }else{
//The animation is over.
                    ......
                }
            }
//Update animations for each application, including those related to exit and removal of apps. The Windows State Animator animation is executed here.
            updateWindowsLocked(displayId);
            updateWallpaperLocked(displayId);
 
 
            final WindowList windows = mService.getWindowListLocked(displayId);
            final int N = windows.size();
            for (int j = 0; j < N; j++) {
//Update the surface.
                windows.get(j).mWinAnimator.prepareSurfaceLocked(true);
            }
        }
//The animation is not over yet. Call scheduleAnimationLocked again.
        if (mAnimating) {
            mService.scheduleAnimationLocked();
        }
    }finally{
//Unified submission to surfaceflinger processing.
        SurfaceControl.closeTransaction();
    }
 
//When performing the last step of the animation, mAnimating is false, and was Animating is the value of the last mAnimating, so for true, a traversal is requested here to calculate the window size, performance urface, etc.
    if (!mAnimating && wasAnimating) {
        mWindowPlacerLocked.requestTraversal();
    }
}

Regardless of the type of animation, the call to update the animation is step Animation Locked, which specifies the single step of the animation and calculates the latest state of the animation at each specific time. If the function is true, the animation is still in progress, otherwise false is returned.

boolean stepAnimationLocked(long currentTime) @WindowStateAnimator.java{
//MWas Animating saves the last state. By comparing mWas Animating with isAnimation Set (), you know whether the animation has just started or stopped. mWasAnimating = mAnimating;
//For the first animation, some initialization is needed.
    if (!mLocalAnimating) {
        mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
            displayInfo.appWidth, displayInfo.appHeight);
        mAnimDx = displayInfo.appWidth;
        mAnimDy = displayInfo.appHeight;
        mAnimation.setStartTime(mAnimationStartTime != -1
            ? mAnimationStartTime : currentTime);
//Once, mLocalAnimating is set to true.
        mLocalAnimating = true;
//Indicates that the animation is currently being executed.
        mAnimating = true;
    }
 
// mAnimation makes non-null judgments, because not every Windows State Animator currently has animation execution.
    if ((mAnimation != null) && mLocalAnimating) {
        mLastAnimationTime = currentTime;
//Execute a single step, calculate the current animation status, return true, indicating that the animation is not over.
        if (stepAnimation(currentTime)) {
            return true;
        }
    }
}

How to perform a step.

private boolean stepAnimation(long currentTime) @WindowStateAnimator.java{
    currentTime = getAnimationFrameTime(mAnimation, currentTime);
    mTransformation.clear();
    final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
    return more;
}

The above function calculates the new transformation value of animation according to the start time of mAnimation setting and the current time. The four elements of animation are translation, zooming, rotation and transparency. In getTransform (), the corresponding type of applyTransform will be invoked according to the set attributes to calculate the transformation value. Save in mTransformation

Among them, translation, zooming and rotation are represented by mMatrix and transparency by mAlpha.

Complete the calculation of animation data, and then it is really displayed on the screen. First, the surface is updated by WMS, specifically the call of prepareSurface Locked.

Finally, the business is closed by SurfaceControl.closeTransaction(), and the updated information in SurfaceFlinger is transmitted uniformly to SurfaceFlinger. Then it is written into framebuffer to complete the display.
 

Reference resources:

Android 7.1 GUI System - Windows Management WMS - Windows Animation, Application Animation Loading (6)

Android 7.1 GUI System - Windows Management WMS - Animation Execution (7)

Android 6.0 WMS (6) WMS Animation Management

Deep Understanding of Android Volume 3

 

 

Tags: Java Android Windows Attribute

Posted on Tue, 27 Aug 2019 20:18:01 -0700 by Paris!