In the last article, the process of obtaining screen direction value was summarized. In this article, the process of obtaining screen direction value will be followed by the analysis of the following functions:

    1. According to the screen direction value + current window properties, determine the display direction;
    1. Logical screen direction update;
    1. The execution of screen rotation animation;

Following the above analysis, when the DisplayRotation detects a screen orientation change, it uses the WindowManagerService#updateRotation() method to notify WMS of the global update. Calling the updateRotationUnchecked() method directly in the WMS#updateRotation() method:

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

    / * * *@paramAlwaysSendConfiguration Whether to update the Configuration *@paramForceRelayout Specifies whether to force the layout operation */
    private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {...try {
            synchronized (mGlobalLock) {
                boolean layoutNeeded = false;
                final int displayCount = mRoot.mChildren.size();
                / / traverse DisplayContent
                for (int i = 0; i < displayCount; ++i) {
                    final DisplayContent displayContent = mRoot.mChildren.get(i);
                    // Enter DisplayContent to update the screen orientation value
                    final boolean rotationChanged = displayContent.updateRotationUnchecked();

                    if (rotationChanged) {
                        mAtmService.getTaskChangeNotificationController()
                                .notifyOnActivityRotation(displayContent.mDisplayId);
                    }
                    // Whether to do layout
                    if(! rotationChanged || forceRelayout) { displayContent.setLayoutNeeded(); layoutNeeded =true;
                    }
                    // Whether to update the Configuration
                    if(rotationChanged || alwaysSendConfiguration) { displayContent.sendNewConfiguration(); }}/ / layout
                if(layoutNeeded) { mWindowPlacerLocked.performSurfacePlacement(); }}}... }Copy the code

In the above method can update on every DisplayContent direction, and is called directly in the DisplayContent DisplayRotation. UpdateRotationUnchecked () method, Then decide whether to update the Configuration and layout according to the returned result and parameters.

DisplayRotation. UpdateRotationUnchecked () method is as follows:

// frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
 
        boolean updateRotationUnchecked(boolean forceUpdate) {
        final int displayId = mDisplayContent.getDisplayId();
         
            // Gets the turn screen animation object for this DisplayContent
            final ScreenRotationAnimation screenRotationAnimation =
                    mDisplayContent.getRotationAnimation();
            // If a screen turn animation is currently in progress, no subsequent action will be performed
            if(screenRotationAnimation ! =null && screenRotationAnimation.isAnimating()) {
                return false;
            }
            // If the current screen is frozen, no updates will be made
            if (mService.mDisplayFrozen) {
                return false;
            }
            // If the top-level Activity executes the most recent task animation and the Activity has a fixed orientation set, no updates will be made
            if (mDisplayContent.mFixedRotationTransitionListener
                    .isTopFixedOrientationRecentsAnimating()) {
                return false; }}// If the WMS startup is not complete, it will not be updated
        if(! mService.mDisplayEnabled) {return false;
        }
        // Current (before update) screen orientation
        final int oldRotation = mRotation;
        // Display direction of the last application
        final int lastOrientation = mLastOrientation;
 
        // Determine a screen orientation based on the given display orientation
        final int rotation = rotationForOrientation(lastOrientation, oldRotation);
       
        // If the screen orientation has not changed, it will not be updated. Otherwise, the screen orientation value has changed
        if (oldRotation == rotation) {
            return false;
        }
        / / if the screen turned not more than two direction of value and the Settings mDisplayContent. MWaitingForConfig is true, in case there is a turn, a small optimization
        if(DisplayContent.deltaRotation(rotation, oldRotation) ! =2) {
            mDisplayContent.mWaitingForConfig = true;
        }
        // Update the current screen orientation
        mRotation = rotation;
        //
        mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
        mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
                mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
        // Indicates that a layout operation is required
        mDisplayContent.setLayoutNeeded();
         
        // Select the Swivel animation
        if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
            // Seamless animation
            prepareSeamlessRotation();
        } else {
            prepareNormalRotationAnimation();
        }
 
        // Send SystemUI a message about the screen orientation change, which is processed in SystemUI's DisplayChangeController and will be called back to the FW
        startRemoteRotation(oldRotation, mRotation);
 
        return true;
    }
Copy the code

In the above method, some judgments are first made to determine whether the screen orientation value needs to be updated, and the update will be made only if the update conditions are met, which are as follows:

  1. If there are currently rotating animations in progress, they will not be updated;
  2. If the current screen is still frozen, it will not update;
  3. If the top-level Activity executes the most recent task animation and the Activity has a fixed orientation set, the update will not occur.
  4. If the WMS startup is not complete, it will not be updated;

If the above four conditions are verified, then a final display direction will be obtained according to the current screen direction value and current display direction (the value before updating), combined with the screen direction value detected by the Sensor and the direction state set by the Activity.

1. Determine the display direction based on the screen direction value plus the current window properties

This part of the process takes place in the rotationForOrientation() method with the following code:

// frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
 
    int rotationForOrientation(@ScreenOrientation int orientation,
            @Surface.Rotation int lastRotation) {
        // If the user fixed the screen orientation, the user set the screen orientation value
        if (isFixedToUserRotation()) {
            return mUserRotation;
        }
        // Get the screen direction value detected by Sensor
        intsensorRotation = mOrientationListener ! =null
                ? mOrientationListener.getProposedRotation() // may be -1
                : -1;
        if (sensorRotation < 0) {
            sensorRotation = lastRotation;
        }
 
        // lid Switch status
        final int lidState = mDisplayPolicy.getLidState();
        // Various dock connection states
        final int dockMode = mDisplayPolicy.getDockMode();
        final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
        final boolean carDockEnablesAccelerometer =
                mDisplayPolicy.isCarDockEnablesAccelerometer();
        final boolean deskDockEnablesAccelerometer =
                mDisplayPolicy.isDeskDockEnablesAccelerometer();
 
        // Indicates the screen orientation preference value to select
        final int preferredRotation;
        if(! isDefaultDisplay) {// The second screen always uses user-set screen orientation values
            preferredRotation = mUserRotation;
        } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
            // If lid_switch open is used, the value specified by lid open is used
            preferredRotation = mLidOpenRotation;
        } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
            ........
        } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
            // The Activity locks the display orientation to any of its current display orientation, so continue to use the current screen orientation value
            preferredRotation = lastRotation;
        } else if(! mSupportAutoRotation) {// Automatic rotation is not supported
            preferredRotation = -1;
        } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE                 
                        // If automatic rotation is enabled and the display direction values meet the following requirements,&& (orientation == ActivityInfo.SCREEN_ORIENTATION_USER || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED ||  orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER)) || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {// mAllowAllRotations indicates whether rotation is allowed in all directions. Some devices cannot rotate 180 degrees because this is false
            if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) {
                mAllowAllRotations = mContext.getResources().getBoolean(
                        R.bool.config_allowAllRotations)
                                ? ALLOW_ALL_ROTATIONS_ENABLED
                                : ALLOW_ALL_ROTATIONS_DISABLED;
            }
            // Sensor Detects that the current screen orientation value is 180 degrees
            if(sensorRotation ! = Surface.ROTATION_180// Allow full rotation
                    || mAllowAllRotations == ALLOW_ALL_ROTATIONS_ENABLED
                    // Display direction is fullsensor
                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR 
                    // Display direction is fullsensor
                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {  
                // If any condition is met, the screen orientation value detected by the sensor will be used preferentially
                preferredRotation = sensorRotation;                                
            } else {   
                // Otherwise, use the current screen orientation valuepreferredRotation = lastRotation; }}else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED  
        // If the orientation rotation is disabled and the last display orientation meets the following conditions, the screen orientation value set by the user is used&& orientation ! = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR && orientation ! = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE && orientation ! = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT && orientation ! = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE && orientation ! = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {// User orientation values are preferred
            preferredRotation = mUserRotation;
        } else {
            // Indicates that there is no preemptive condition
            preferredRotation = -1;
        }
 
        // Based on the direction of the last display
        switch (orientation) {
            // Display direction is vertical
            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:  
                if (isAnyPortrait(preferredRotation)) {
                    return preferredRotation;
                }
                return mPortraitRotation;
 
            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
                if (isLandscapeOrSeascape(preferredRotation)) {
                    return preferredRotation;
                }
                return mLandscapeRotation;
 
            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
                if (isAnyPortrait(preferredRotation)) {
                    return preferredRotation;
                }
                return mUpsideDownRotation;
 
            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
                if (isLandscapeOrSeascape(preferredRotation)) {
                    return preferredRotation;
                }
                return mSeascapeRotation;
 
            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
                if (isLandscapeOrSeascape(preferredRotation)) {
                    return preferredRotation;
                }
                if (isLandscapeOrSeascape(lastRotation)) {
                    return lastRotation;
                }
                return mLandscapeRotation;
 
            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
                if (isAnyPortrait(preferredRotation)) {
                    return preferredRotation;
                }
                if (isAnyPortrait(lastRotation)) {
                    return lastRotation;
                }
                return mPortraitRotation;
 
            default:
                // If the directions are USER, UNSPECIFIED, NOSENSOR, SENSOR, or FULL_SENSOR, the preferredRotation value or 0 degrees is returned
                if (preferredRotation >= 0) {
                    return preferredRotation;
                }
                returnSurface.ROTATION_0; }}Copy the code

In the above method, first determine a preferred screen orientation based on various system states, and then confirm the final screen orientation based on the display orientation set by the last Activity. The decision-making process of these processes is summarized as follows:

First determine the priority screen orientation value:

  1. If the user fixes the screen orientation value, the user directly returns the screen orientation value. Fixed screen orientation for useWMS#setFixedToUserRotation()Methods;
  2. If it is not the default screen, the screen orientation value set by the user is directly returned. throughWMS#freezeDisplayRotation(int displayId, int rotation)Method to set a specified screen orientation value;
  3. If lid is in OPEN state and a specified screen direction is configured for this mode, the specified screen direction value when lid_switch OPEN is used preferentially. Lid_switch Is reported by InputManger. For example, hall sensor events are reported by lid_switch.
  4. If the device is in any Dock mode and the specified screen orientation is configured for this mode, the specified screen orientation is preferred. There are few such scenarios, so we will skip its process here.
  5. If the Activity is currently set to LOCKED, which means that the screen orientation is LOCKED to any of its current display directions, the current screen orientation value is preferred. Activity shows direction by the android: screenOrientation = “locked” or Activity# setRequestedOrientation (int requestedOrientation) method set, the same below;
  6. If auto rotation is not supported, the variable indicating the preferred screen orientation will be set to -1. Configure whether automatic rotation is supported by config_supportAutoRotation in config. XML.
  7. If automatic rotation is enabled, and the orientation of the Activity is Set to USER, UNSPECIFIED, USER_LANDSCAPE, USER_PORTRAIT, or FULL_USER, the screen orientation obtained from the Sensor or the latest screen orientation is preferred. These directions can be adjusted according to the Sensor.
  8. If the Activity is set to SENSOR, FULL_SENSOR, SENSOR_LANDSCAPE, SENSOR_PORTRAIT, the same as above.
  9. If auto rotation is turned off and the display orientation values set by the Activity are NOSENSOR, LANDSCAPE, PORTRAIT, REVERSE_LANDSCAPE, REVERSE_PORTRAIT, the screen orientation set by the user is preferred.
  10. If none of the above conditions are met, the priority screen orientation variable will be set to -1.

Secondly, determine the final screen orientation value:

  1. If the last time the Activity was set to a display orientation value of PORTRAIT, the screen orientation normally returns surface.rotation_0.
  2. If the last time the Activity was set to LANDSCAPE, the screen orientation normally returns surface.rotation_90.
  3. If the display orientation value of the last Activity setting was REVERSE_PORTRAIT, the screen orientation normally returns surface.rotATION_180.
  4. If the last time the Activity was set to REVERSE_LANDSCAPE, the screen orientation normally returns to Surface.rotATION_270.
  5. If the last time the Activity was set to a display orientation value of SENSOR_LANDSCAPE or USER_LANDSCAPE, the screen orientation generally returns surface.rotATION_90 or Surface.rotation_270.
  6. If the last time the Activity was set to a display orientation value of SENSOR_PORTRAIT or USER_PORTRAIT, the screen orientation generally returns ROTATION_0 or surf. ROTATION_270.
  7. If none of the above conditions are met, the screen orientation returns the priority value if the priority screen orientation is greater than or equal to 0, otherwise surface.rotATION_0 is returned.

These steps above looks is very afflictive, but in fact this logic is the android: screenOrientation attribute function implementation, know about the individual value of this property is a natural understand. After the rotationForOrientation() method, a screen orientation value is obtained.

Returning to the DisplayRotation#updateRotationUnchecked() method, if the resulting screen orientation values change, you’ll need to update the display orientation. Next, you’ll select the Screen rotation animation to perform the rotation animation.

2. Determine the screen rotation mode and the screen rotation animation type

Screen rotation is divided into two types according to whether there is animation or not:

  1. One is Seamless Rotation, which transitions from one direction to another without freezing the screen.
  2. One is Normal Rotation, which, when used, takes a screenshot of the old orientation and freezes the screen, and after the update is complete, animations slowly transition to the new orientation.

According to animation execution way, and can be divided into four categories, and these four class is used to be, through the WindowManager. LayoutParams. RotationAnimation attribute to specify:

  1. ROTATION_ANIMATION_ROTATE: the default operation mode.
  2. ROTATION_ANIMATION_CROSSFADE: methods of fade-in and fade-out;
  3. ROTATION_ANIMATION_JUMPCUT: Jumps/jumps immediately when the current window is displayed or exits;
  4. ROTATION_ANIMATION_SEAMLESS: Use Seamless Rotation, but if Seamless Rotation is not supported, use CROSSFADE;

The above four values can only be used for full-screen opaque Windows.

After the screenroll process is triggered, the first step is to select the type of screenroll animation. ShouldRotateSeamlessly () method is used to determine whether Seamless Rotation should be used:

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

    / * * *@paramOldRotation Old screen orientation *@paramNewRotation New screen orientation *@paramForceUpdate Whether to force an update *@returnTrue: Use seamless Rotation */
    boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
        if (mDisplayContent.hasTopFixedRotationLaunchingApp()) {
            return true;
        }
 
        // Get the current full-screen opaque window
        final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
        If the current full-screen opaque window has no focus, do not use Seamless Rotation
        if (w == null|| w ! = mDisplayContent.mCurrentFocus) {return false;
        }
        If the top-level window animation property is not ROTATION_ANIMATION_SEAMLESS, or if the animation is executing, do not use Seamless Rotation
        if(w.getAttrs().rotationAnimation ! = ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) {return false;
        }
        // If screen orientation values are 180 degrees relative to each other, don't use Seamless Rotation
        if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
            return false;
        }
        // The other scenarios are not common, so leave them out.If the mSeamlesslyRotated property of the current full-screen opaque window is set to false, don't use Seamless Rotation
        if(! forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) ! =null) {
            return false;
        }
         // If the above conditions are not met, return true
        return true;
    }
Copy the code

Under Normal circumstances, except for special scenes, most scenes use Normal Rotation, that is, when you rotate the screen, you animate it.

Using Normal Rotation, the first by prepareNormalRotationAnimation () method to choose animation execution way, and then began to freeze the screen:

// frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
 
    void prepareNormalRotationAnimation(a) {
        // Cancel the ongoing Seamless Rotation
        cancelSeamlessRotation();
        // Select the animation execution mode
        final RotationAnimationPair anim = selectRotationAnimation();
        // Freeze the screen
        mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);
    }
Copy the code

In selectRotationAnimation () method of choosing animation execution way, according to the current top-level Windows WindowManager. LayoutParams# rotationAnimation to select a set of animation, choose animation implementation process is simpler, I’m just going to skip it.

App if you want to operation screen animation, through the WindowManager. LayoutParams# rotationAnimation control, is the selectRotationAnimation () choice here

3. Freeze the screen

If Seamless Rotation is not used, the screen will freeze once the animation mode is determined. Freezing the screen is to freeze the display content on the current content, the implementation principle of freezing is very simple:

  1. Freeze Input events;
  2. Take a screenshot of the current screen and display it on the top layer;

The reason why the screen needs to be frozen during the screen rotation is that the screen can be frozen and unfrozen in the form of animation. After the actual rotation is completed, the screen can be unfrozen and returned to the new display direction in the form of animation, which provides better user experience. And the realization of the turn screen animation, and the screen freeze bound together. A special analysis of screen freezes will follow.

At this point, the new screen orientation, rotation animation type and mode are determined, and the screen is frozen.

4. Notify SystemUI of the direction change

Returning to the DisplayRotation#updateRotationUnchecked(), you’ll execute the startRemoteRotation() method, which notifies SystemUI of the rotation information and lets it do the corresponding processing:

// frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
 
    private void startRemoteRotation(int fromRotation, int toRotation) {
        // Indicates that the client is waiting for a callback
        mIsWaitingForRemoteRotation = true;
        try {
            // Notifies the SystemUI module of orientation rotation information
            mService.mDisplayRotationController.onRotateDisplay(mDisplayContent.getDisplayId(),
                    fromRotation, toRotation, mRemoteRotationCallback);
        } catch (RemoteException e) {
        }
    }
Copy the code

When the SystemUI after receiving the call and processed, can callback DisplayRotation mRemoteRotationCallback# continueRotateDisplay () method to notify the WMS module.

At this point, the DisplayRotation#updateRotationUnchecked() method completes and returns true. Next you wait for SystemUI’s callback……

5. When SystemUI receives the call, it calls back to system_server

When a SystemUI callback or a callback timeout is received, the DisplayRotation#continueRotation() method is executed to continue the rotation process:

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
 
    private void continueRotation(int targetRotation, WindowContainerTransaction t) {
        synchronized (mService.mGlobalLock) {
            mIsWaitingForRemoteRotation = false;
            // defer layout
            mService.mAtmService.deferWindowLayout();
            try {
                // The Configuration is updated
                mDisplayContent.sendNewConfiguration();
            } finally {
                // continue layoutmService.mAtmService.continueWindowLayout(); }}}Copy the code

In the above methods, the DisplayContent#sendNewConfiguration() method is used to update the new Configuration.

6. The logical screen configuration is updated

The update entry method of the Configuration object is as follows:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
 
    void sendNewConfiguration(a) {...final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
        // When configUpdated is true, subsequent processes are not executed
        if (configUpdated) {
            return; }... }Copy the code

Starting with the sendNewConfiguration() method, the global configuration will be updated. This part of the process will not be described in detail here, just a brief introduction.

In sendNewConfiguration () method, is called updateDisplayOverrideConfigurationLocked () method to update the Display cover the Configuration object:

  1. A new Configuration object is created;
  2. Then, incomputeScreenConfiguration()Method to fill the Configuration object with screen related properties such as range boundaries, screen density, layout, keyboard, logical screen information, and so on…..

When the logical screen information is updated, the screen width and height are set according to the DisplayRotation#mRotation:

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
 
    private DisplayInfo updateDisplayAndOrientation(int uiMode, Configuration outConfig) {
        // Get the screen orientation value from the DisplayRotation
        final int rotation = getRotation();
        // When the screen orientation value is 1(90 degrees) and 3(270 degrees), the screen width and height need to be changed
        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
        // Get logical Display width height based on height and width
        final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
        final intdh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight; .// Set the screen orientation valuemDisplayInfo.rotation = rotation; .// Set the logical screen information in WMS to DMSmWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId, overrideDisplayInfo); .return mDisplayInfo;
    }
Copy the code

At this point, if the orientation is rotated, the width and height of the logical screen will be swapped.

  1. After populating the newly created Configuration object, passupdateDisplayOverrideConfigurationLocked()Method further executes the process of updating the Override configuration. In this method, if it is Default Display, not only the Override Configuration but also the global Configuration need to be updated. If it is a non-default Display, only override Configuration is updated:
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    boolean updateDisplayOverrideConfigurationLocked(Configuration values,
            ActivityRecord starting, boolean deferResume,
            ActivityTaskManagerService.UpdateConfigurationResult result) {... mAtmService.deferWindowLayout();try {
            if(values ! =null) {
                if (mDisplayId == DEFAULT_DISPLAY) {
                    // Update the global Configuration
                    changes = mAtmService.updateGlobalConfigurationLocked(values,
                            false /* initLocale */.false /* persistent */,
                            UserHandle.USER_NULL /* userId */, deferResume);
                } else {
                    // Update Override Configurationchanges = performDisplayOverrideConfigUpdate(values, deferResume); }}}return kept;
    }
Copy the code

Global Configuration updates through ATMS# updateGlobalConfigurationLocked () method, Override the Configuration update through DisplayContent# performDisplayOverrideConfigUpdate () method.

In the update Override the Configuration process, can be in onRequestedOverrideConfigurationChanged () method in the global variable mRequestedOverrideConfiguration update, In addition, it will determine whether the screen direction changes, and then update the direction:

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

    public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
        // Obtain the current Override Configuration
        final Configuration currOverrideConfig = getRequestedOverrideConfiguration();
        final int currRotation = currOverrideConfig.windowConfiguration.getRotation();
        final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation();
        // Screen orientation changes
        if(currRotation ! = ROTATION_UNDEFINED && currRotation ! = overrideRotation) { applyRotationAndFinishFixedRotation(currRotation, overrideRotation); } mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);/ / update the mRequestedOverrideConfiguration
        super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
        mCurrentOverrideConfigurationChanges = 0;
        mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this);
        mAtmService.addWindowLayoutReasons(
                ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
    }
Copy the code

If the mRotation property of the current Override Configuration object is inconsistent with that of the new Override Configuration object, the screen direction has changed. What will will be through DC# applyRotationAndFinishFixedRotation () method to update the screen display orientation, this method and by applyRotation () method.

7. Use the new screen orientation

In the applyRotation() method, the properties are updated using the new screen orientation, and a request is made to the DMS to configure the logical screen:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
 
    private void applyRotation(final int oldRotation, final int rotation) {
         
        / / update WindowOrientationListener# mCurrentRotation variables
        mDisplayRotation.applyCurrentRotation(rotation);
        // Seamless rotation has no animation
        final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly();
        final Transaction transaction = getPendingTransaction();
        // Rotate the screen animation object
        ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
                ? null : getRotationAnimation();
        // Update the logical screen information to ensure that the screen orientation is correct
        updateDisplayAndOrientation(getConfiguration().uiMode, null /* outConfig */);
        // The rotation animation sets the initial position matrix
        if(screenRotationAnimation ! =null && screenRotationAnimation.hasScreenshot()) {
            screenRotationAnimation.setRotation(transaction, rotation);
        }
        // Update properties related to Seamless Rotation in WindowState
        forAllWindows(w -> {
            w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
        }, true /* traverseTopToBottom */);
        // Call DMS to update the Display configuration
        mWmService.mDisplayManagerInternal.performTraversal(transaction);
        // Perform the animation
        scheduleAnimation();
         
        // update the WindowState#mOrientationChanging property
        forAllWindows(w -> {
            if(w.mHasSurface && ! rotateSeamlessly) { w.setOrientationChanging(true);
                / / mOrientationChangeComplete shows the direction of the update is complete
                mWmService.mRoot.mOrientationChangeComplete = false;
                w.mLastFreezeDuration = 0;
            }
            // Indicates that the client needs to be notified of a screen turn
            w.mReportOrientationChanged = true;
        }, true /* traverseTopToBottom */);
 
        / / initiate RotationWatcher# onRotationChanged ()
        for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) {
            final WindowManagerService.RotationWatcher rotationWatcher
                    = mWmService.mRotationWatchers.get(i);
            if (rotationWatcher.mDisplayId == mDisplayId) {
                try {
                    rotationWatcher.mWatcher.onRotationChanged(rotation);
                } catch (RemoteException e) {
                    // Ignore}}}}Copy the code

Among the above methods, the main processes are as follows:

  1. First by DisplayRotation# applyCurrentRotation () method, update WindowOrientationListener# mCurrentRotation variables;
  2. If Seamless Rotation is used, no animation is required; Otherwise, a screen rotation is required, and the transition matrix for the initial screen rotation is set. Then the WindowState is iterated and the WindowState#mOrientationChanging property is set to true, indicating that the screen rotation is in progress.
  3. performupdateDisplayAndOrientation()Update logical screen properties.
  4. throughmDisplayManagerInternal.performTraversal()Update the Display attribute.
  5. Next, I’m going tomWmService.mRoot.mOrientationChangeCompleteThe value is set to false. This value indicates whether the update of the display direction is complete and will be set to true when the rotation animation is complete to begin the unfreezing process.
  6. Finally, a RotationWatcher#onRotationChanged() call is issued to notify all RotationWatcher directions that have changed.

At this point, the process in the applyRotation() method completes.

The next step is to wait for the arrival of the VSync signal for the refresh positioning process, in which the screen is unfrozen and a screen-turn animation is performed.

8. Unfreeze the screen and perform the screen rotation animation

Then, when the WindowAnimator object receives the VSync signal, it animates. In this animation:

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

    private void animate(long frameTimeNs) {...// the SET_ORIENTATION_CHANGE_COMPLETE flag bit indicates that the direction change is complete
        mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
        // Start the transactionmService.openSurfaceTransaction(); .final boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
        / / update mOrientationChangeComplete properties
        final booleandoRequest = mBulkUpdateParams ! =0 && mService.mRoot.copyAnimToLayoutParams();
        // Initiate WMSS traversal
        if(hasPendingLayoutChanges || doRequest) { mService.mWindowPlacerLocked.requestTraversal(); }... SurfaceControl.mergeToGlobalTransaction(mTransaction);// Close the transaction and commit
        mService.closeSurfaceTransaction("WindowAnimator");
    }
Copy the code

In copyAnimToLayoutParams () method, will update will mOrientationChangeComplete attribute to true, said display direction is updated:

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

    boolean copyAnimToLayoutParams(a) {
        boolean doRequest = false;

        final intbulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams; .if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
            mOrientationChangeComplete = false;
        } else {
            mOrientationChangeComplete = true;
            mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
            if(mWmService.mWindowsFreezingScreen ! = WINDOWS_FREEZING_SCREENS_NONE) { doRequest =true; }}...return doRequest;
    }
Copy the code

When this value is set to true, the thawing process is performed during subsequent traversal operations.

Thawing of the screen by WMS# stopFreezingDisplayLocked () method, when the WMS module components when performing localization process, after receiving the VSync signal will be thawing process of this method is called:

    void performSurfacePlacementNoTrace(a) {...// Show whether the direction change is complete
        if (mOrientationChangeComplete) {
            if(mWmService.mWindowsFreezingScreen ! = WINDOWS_FREEZING_SCREENS_NONE) { mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE; mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource; mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT); }/ / thawingmWmService.stopFreezingDisplayLocked(); }... }Copy the code

Whether can be seen in the above logic, thawing depends on mOrientationChangeComplete attribute, the value in the previous step in the process has been set to true, see specific thawing process below:

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

    void stopFreezingDisplayLocked(a) {...// Reset mDisplayFrozen to false to indicate that it is not frozen
        mDisplayFrozen = false;
        / / thaw IMS
        mInputManagerCallback.thawInputDispatchingLw();

        boolean updateRotation = false;
        // Get ScreenRotationAnimation object
        ScreenRotationAnimation screenRotationAnimation = displayContent == null ? null
                : displayContent.getRotationAnimation();
        if(screenRotationAnimation ! =null && screenRotationAnimation.hasScreenshot()) {
            DisplayInfo displayInfo = displayContent.getDisplayInfo();
            // Get rotation animation again, with new top window
            if(! displayContent.getDisplayRotation().validateRotationAnimation( mExitAnimId, mEnterAnimId,false /* forceDefault */)) {
                mExitAnimId = mEnterAnimId = 0;
            }
            // Execute the screen-turn animation
            if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
                    getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                        displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
                mTransaction.apply();
            // Direct transition
            } else {
                screenRotationAnimation.kill();
                displayContent.setRotationAnimation(null);
                updateRotation = true; }}else {
            if(screenRotationAnimation ! =null) {
                screenRotationAnimation.kill();
                displayContent.setRotationAnimation(null);
            }
            updateRotation = true; }... }Copy the code

In the above method, the screen is unfrozen and the screen turn animation is performed. When the screen rotation animation is completed, the entire rotation process of the display direction is completed.