Android U WindowManagerService 屏幕旋转动画(1)

2,924 阅读13分钟

案例

  1. 在下拉控制中心打开屏幕旋转开关。
  2. 用 Android Studio 创建一个 HelloWorld app,然后运行起来。
  3. 在 MainActivity 界面,把手机从竖屏旋转到横屏,会看到一个屏幕旋转动画。

获取 sensor上报的方向

WMS 通过监听 Sensor 得知设备方向改变

// WindowOrientationListener.java

final class OrientationSensorJudge extends OrientationJudge {
    
    public void onSensorChanged(SensorEvent event) {
        // 1.获取 sensor 上报的旋转方向
        int reportedRotation = (int) event.values[0];

        // ...
        
        if (isRotationResolverEnabled()) {
            // ...
        } else {
            finalizeRotation(reportedRotation);
        }
    }

    private void finalizeRotation(int reportedRotation) {
        int newRotation;
        synchronized (mLock) {
            mDesiredRotation = reportedRotation;
            
            // 2. 计算旋转方向,一般来说就是 sensor 上报的方向
            newRotation = evaluateRotationChangeLocked();
        }
        
        if (newRotation >= 0) {
            mLastRotationResolution = newRotation;
            mLastRotationResolutionTimeStamp = SystemClock.uptimeMillis();
            
            // 3. 处理旋转方向改变,由宿主类 DisplayRotation 实现
            onProposedRotationChanged(newRotation);
        }
    }
}
// DisplayRotation.java

private class OrientationListener extends WindowOrientationListener implements Runnable {
    public void onProposedRotationChanged(@Surface.Rotation int rotation) {
        ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
        
        // ..
        
        if (isRotationChoiceAllowed(rotation)) {
            // ...
        } else {
            mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
            
            // 4. WMS 更新旋转
            mService.updateRotation(false /* alwaysSendConfiguration */,
                    false /* forceRelayout */);
        }
    }
}

WMS 更新旋转

// WindowManagerService.java

// 两个参数,都是false
public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
    updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
}

private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
    ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:"
                    + " alwaysSendConfiguration=%b forceRelayout=%b",
            alwaysSendConfiguration, forceRelayout);

    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");
    final long origId = Binder.clearCallingIdentity();
    try {
        synchronized (mGlobalLock) {
            boolean layoutNeeded = false;
            final int displayCount = mRoot.mChildren.size();
            for (int i = 0; i < displayCount; ++i) {
                final DisplayContent displayContent = mRoot.mChildren.get(i);
                
                // DisplayContent 更新旋转
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");         
                final boolean rotationChanged = displayContent.updateRotationUnchecked();
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

                // ...
            }           
            // ...
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
}
// DisplayContent.java

boolean updateRotationUnchecked() {
    return mDisplayRotation.updateRotationUnchecked(false /* forceUpdate */);
}
// DisplayRotation.java

// forceUpdate 为 false
boolean updateRotationUnchecked(boolean forceUpdate) {    
    // ...

    final int oldRotation = mRotation;
    final int lastOrientation = mLastOrientation;
    
    // 1. 计算旋转方向
    int rotation = rotationForOrientation(lastOrientation, oldRotation);
    
    // ...

    ProtoLog.v(WM_DEBUG_ORIENTATION,
            "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
                    displayId, rotation, oldRotation, lastOrientation);
    
    // 保存计算出的新的旋转方向
    mRotation = rotation;
    // 标记 DisplayContent 需要 layout 
    mDisplayContent.setLayoutNeeded();
    // 标记 DisplayContent 正在等待配置更新
    mDisplayContent.mWaitingForConfig = true;

    if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
        // ...
        
        final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
                : new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
                        oldRotation, mRotation);

        // 2. 请求 Transition
        mDisplayContent.requestChangeTransitionIfNeeded(
                ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change);
        
        // ...
        
        return true;
    }

    // ...
}

计算旋转方向

MainActivity 没有声明/请求 orientation,orientation 默认为 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,sensor 上报的方向是 90°,最终计算出的旋转方向为 Surface.ROTATION_90,如下

// DisplayRotation.java

// orientation 为 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
int rotationForOrientation(@ScreenOrientation int orientation,
        @Surface.Rotation int lastRotation) {
    // ...


    // 获取sensor上报的方向
    int sensorRotation = mOrientationListener != null
            ? mOrientationListener.getProposedRotation() // may be -1
            : -1;
    
    // ...
    
    final int preferredRotation;
    if (/* QTI_BEGIN */ !(overrideMirroring && isBuiltin) && /* QTI_END */ !isDefaultDisplay) {

    } // ...
    else if (((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE//旋转开关打开
                        || isTabletopAutoRotateOverrideEnabled())
                    && (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) {
        // sensorRotation 是 Surface.ROTATION_90
        if (sensorRotation != Surface.ROTATION_180
                || getAllowAllRotations() == ALLOW_ALL_ROTATIONS_ENABLED
                || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
                || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
            // 使用 sensor 方向作为优先的旋转方向
            preferredRotation = sensorRotation;
        } else {
            
        }
    }// ...
    
    switch (orientation) {
        // ...

        default:
            if (preferredRotation >= 0) {
                return preferredRotation;
            }
            // ...
    }
}

请求 Transition

// DisplayContent.java

// 参数 changes 值为 ActivityInfo.CONFIG_WINDOW_CONFIGURATION
// 参数 displayChange 保存旋转前后的方向
void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes,
        @Nullable TransitionRequestInfo.DisplayChange displayChange) {
    // ...
    
    // 1. 请求类型为 CHANGE 的 Transition
    final Transition t = controller.requestTransitionIfNeeded(TRANSIT_CHANGE, 0 /* flags */,
            this, this, null /* remoteTransition */, displayChange);
    
    if (t != null) {
        mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
        if (mFixedRotationLaunchingApp != null) {
            // ...
        } else if (isRotationChanging()) {  
            // ...
            
            // 启动异步旋转,不在本文分析范围内
            startAsyncRotation(false /* shouldDebounce */);
        }
        
        // 2. DisplayContent 的 ChangeInfo#mKnownConfigChanges
        // 更新为 ActivityInfo.CONFIG_WINDOW_CONFIGURATION
        t.setKnownConfigChanges(this, changes);
    }
}
// TransitionController.java

// type 为 TRANSIT_CHANGE
// flags 为 0
// trigger 为 DisplayContent
// readyGroupRef 为 DisplayContent
// remoteTransition 为 null
// displayChange 保存了旋转前后的方向
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
        @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
        @NonNull WindowContainer readyGroupRef, @Nullable RemoteTransition remoteTransition,
        @Nullable TransitionRequestInfo.DisplayChange displayChange) {
    // ...
    
    Transition newTransition = null;
    if (isCollecting()) {
        
    } else {
        // 1. 创建 Transition,并向 WM-Shell 发起请求
        newTransition = requestStartTransition(createTransition(type, flags),
                trigger != null ? trigger.asTask() : null, remoteTransition, displayChange);
        
        // ...
    }
    
    // trigger 为 DisplayContent
    if (trigger != null) {
        if (isExistenceType(type)) {
            // ...
        } else {
            // 2.Transition 收集 DisplayContent
            collect(trigger);
        }
    }
    return newTransition;
}


Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask,
        @Nullable RemoteTransition remoteTransition,
        @Nullable TransitionRequestInfo.DisplayChange displayChange) {
    // ...
    
    try {
        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                "Requesting StartTransition: %s", transition);
        ActivityManager.RunningTaskInfo info = null;
        // startTask 为 null
        if (startTask != null) {
            // ...
        }
        
        // 创建 TransitionRequestInfo,保存 Transition 信息
        // transition.mType 值为 TRANSIT_CHANGE
        // info 是 task 信息,此时为 null
        // remoteTransition 为 null
        // displayChange 仅仅保存了旋转前后的方向
        final TransitionRequestInfo request = new TransitionRequestInfo(
                transition.mType, info, remoteTransition, displayChange);
        transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
        transition.mLogger.mRequest = request;
        
        // 向 wm-shell 发起 transition 请求
        mTransitionPlayer.requestStartTransition(transition.getToken(), request);
        if (remoteTransition != null) {
            
        }
    } catch (RemoteException e) {
        
    }
    return transition;
}

Wm-Shell 处理 Transition 请求

Wm-Shell 收到请求后,主要是建立自己 Transition 数据,然后通知 Wm-Core 执行 start transition。

// Transitions.java

void requestStartTransition(@NonNull IBinder transitionToken,
        @Nullable TransitionRequestInfo request) {
    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
            transitionToken, request);
    
    // ...
    
    final ActiveTransition active = new ActiveTransition();

    // ...
    
    // 1. 通知 WM-Core start transition
    mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
    
    // 2. mPendingTransitions 保存 Transition 数据
    active.mToken = transitionToken;
    mPendingTransitions.add(0, active);
}

WM-Core start transition

// WindowOrganizerController.java

// t 虽然不为 null,但是数据为空
public void startTransition(@NonNull IBinder transitionToken,
        @Nullable WindowContainerTransaction t) {
    startTransition(-1 /* unused type */, transitionToken, t);
}

private IBinder startTransition(@WindowManager.TransitionType int type,
        @Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t) {
    enforceTaskPermission("startTransition()");
    final CallerInfo caller = new CallerInfo();
    final long ident = Binder.clearCallingIdentity();
    try {
        synchronized (mGlobalLock) {
            // 根据 token 获取 Transition
            Transition transition = Transition.fromBinder(transitionToken);
            
            // ...
            
            // wct 此时为 null
            final WindowContainerTransaction wct =
                    t != null ? t : new WindowContainerTransaction();
            
            // ...
            
            // 1. start transition
            transition.start();
            
            transition.mLogger.mStartWCT = wct;
            
            // 2. apply WCT
            applyTransaction(wct, -1 /*syncId*/, transition, caller);
            
            return transition.getToken();
        }
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}    

private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
        @Nullable Transition transition, @NonNull CallerInfo caller) {
    return applyTransaction(t, syncId, transition, caller, null /* finishTransition */);
}

private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
        @Nullable Transition transition, @NonNull CallerInfo caller,
        @Nullable Transition finishTransition) {
    int effects = TRANSACT_EFFECTS_NONE;
    ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
    // 推迟窗口刷新
    mService.deferWindowLayout();
    // 推迟 Activity 可见性更新
    mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
    try {
    
        // apply display change
        if (transition != null) {
            transition.applyDisplayChangeIfNeeded();
        }
        
        // ...
    } finally {
        mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
        mService.continueWindowLayout();
    }
    return effects;
}    
// Transition.java

void applyDisplayChangeIfNeeded() {
    for (int i = mParticipants.size() - 1; i >= 0; --i) {
        // Transition 有收集过 DisplayContent
        final WindowContainer<?> wc = mParticipants.valueAt(i);
        final DisplayContent dc = wc.asDisplayContent();
        if (dc == null || !mChanges.get(dc).hasChanged()) continue;
        
        // 1. DisplayContent更新配置
        dc.sendNewConfiguration();

        if (!mReadyTracker.mUsed) {
            // 2. set ready
            setReady(dc, true);
        }
    }
}

DisplayContent 更新配置

// DisplayContent.java

void sendNewConfiguration() {
    // ...
    
    final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
    if (configUpdated) {
        return;
    }

    // ...
}

boolean updateDisplayOverrideConfigurationLocked() {
    // ...
    
    // 1. 计算新配置
    Configuration values = new Configuration();
    computeScreenConfiguration(values);
    
    // ...
    
    // 2. 更新 display override config
    updateDisplayOverrideConfigurationLocked(values, null /* starting */,
            false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
    
    // true
    return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
}    

首先,主要根据新的旋转方向,计算新配置

// DisplayContent.java

void computeScreenConfiguration(Configuration config) {
    // 根据旋转方向,更新 DisplayContent#mDisplayInfo,
    // 同时也会填充数据到 config
    final DisplayInfo displayInfo = updateDisplayAndOrientation(config);
    
    final int dw = displayInfo.logicalWidth;
    final int dh = displayInfo.logicalHeight;
    mTmpRect.set(0, 0, dw, dh);
    config.windowConfiguration.setBounds(mTmpRect);
    config.windowConfiguration.setMaxBounds(mTmpRect);
    
    // ...

    computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation); 
    
    // ...
}

private DisplayInfo updateDisplayAndOrientation(Configuration outConfig) {
    // 获取 DisplayRotation 计算的旋转方向
    final int rotation = getRotation();
    
    final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
    final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
    final int dh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
    // ...
    // 更新 mDisplayInfo
    mDisplayInfo.rotation = rotation;
    mDisplayInfo.logicalWidth = dw;
    mDisplayInfo.logicalHeight = dh;
    // ...
    

    // 把 mDisplayInfo 更新到 DMS, DMS 会通知 WMS 发起窗口刷新,窗口刷新会收集 DMS 的 transcation 到 DisplayContent sync transcation,从而在 ST 中 apply。
    mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
            mDisplayInfo);
    
    // ...
    
    // 更新 DisplayFrames、system gesture 等
    onDisplayInfoChanged();

    return mDisplayInfo;
}

private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,
        int rotation) {
    final DisplayPolicy.DecorInsets.Info info =
            mDisplayPolicy.getDecorInsetsInfo(rotation, dw, dh);
    // AppBounds at the root level should mirror the app screen size.
    outConfig.windowConfiguration.setAppBounds(info.mNonDecorFrame);
    outConfig.windowConfiguration.setRotation(rotation);
    outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;

    // ...

    final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
    outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dw, dh);
    outConfig.windowConfiguration.setDisplayRotation(rotation);
}

然后,根据新配置,更新 DisplayContent 的 override config

// DisplayContent.java

// values 为计算出的新配置
// starting 为 null
// deferResume 为 false
boolean updateDisplayOverrideConfigurationLocked(Configuration values,
        ActivityRecord starting, boolean deferResume,
        ActivityTaskManagerService.UpdateConfigurationResult result) {
    // ...

    mAtmService.deferWindowLayout();
    try {
        if (values != null) {
            if (mDisplayId == DEFAULT_DISPLAY) {
                // Override configuration of the default display duplicates global config, so
                // we're calling global config update instead for default display. It will also
                // apply the correct override config.
                // 1. 先从 ATMS 的 global config 开始更新
                changes = mAtmService.updateGlobalConfigurationLocked(values,
                        false /* initLocale */, false /* persistent */,
                        UserHandle.USER_NULL /* userId */);
            } else {
                // ...
            }
        }

        if (!deferResume) {
            // 2. 确保 top activity 能处理配置改变
            // 由于 apply WCT 时,推迟了可见性更新,因此这里不会执行可见性更新
            kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
        }
    } finally {
        mAtmService.continueWindowLayout();
    }

    // ...
}

要更新 DisplayContent 的配置,首先 ATMS 计算 global config

// ActivityTaskManagerService.java

int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
        boolean persistent, int userId) {
    // 1. 计算新的 global config
    mTempConfig.setTo(getGlobalConfiguration());//global config 就是 RWC full config
    final int changes = mTempConfig.updateFrom(values);
    
    // ...

    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateGlobalConfiguration");
    ProtoLog.i(WM_DEBUG_CONFIGURATION, "Updating global configuration "
            + "to: %s", values);
    
    // ...

    mTempConfig.seq = increaseConfigurationSeqLocked();

    Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);

    // ...

    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RootConfigChange");
    // 2. 把新的 global config 发给 RWC
    mRootWindowContainer.onConfigurationChanged(mTempConfig);
    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    return changes;
}

得到新的 global config 后,发送给 RWC,让其先更新配置

RWC#onConfigurationChanged() 由基类 ConfigurationContainer 提供实现。这是一个标准的配置更新函数,RWC 主要是把它的 full config 发送给 DisplayContent

// ConfigurationContainer.java

public void onConfigurationChanged(Configuration newParentConfig) {
    // ...
    
    mFullConfiguration.setTo(newParentConfig);
    mFullConfiguration.windowConfiguration.unsetAlwaysOnTop();
    mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
    
    // ...
    
    // 把 full config 发送给 child,即 DisplayContent
    for (int i = getChildCount() - 1; i >= 0; --i) {
        // RWC 有实现这个函数
        dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
    }
}  
// RootWindowContainer.java

void dispatchConfigurationToChild(DisplayContent child, Configuration config) {
    if (child.isDefaultDisplay) {
        // The global configuration is also the override configuration of default display.
        // 先更新 DisplayContent override config
        child.performDisplayOverrideConfigUpdate(config);
    } else {
        // ...
    }
}

RWC 把它的 full config 发送给 DisplayContent,让其先更新 override config

// DisplayContent.java

// values 是 RWC 的 full config
int performDisplayOverrideConfigUpdate(Configuration values) {
    // 1. 计算新的 override config
    mTempConfig.setTo(getRequestedOverrideConfiguration());
    final int changes = mTempConfig.updateFrom(values);
    
    if (changes != 0) {
        Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
                + mTempConfig + " for displayId=" + mDisplayId);
        
        // ...

        // 2. 更新 override config
        onRequestedOverrideConfigurationChanged(mTempConfig);
        
        // ...
    }
    return changes;
}


public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
    final Configuration currOverrideConfig = getRequestedOverrideConfiguration();
    final int currRotation = currOverrideConfig.windowConfiguration.getRotation();
    final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation();

    // 1. 旋转方向改变了,应用新的旋转方向
    if (currRotation != ROTATION_UNDEFINED && overrideRotation != ROTATION_UNDEFINED
            && currRotation != overrideRotation) {
        applyRotationAndFinishFixedRotation(currRotation, overrideRotation);
    }

    mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
    // 2. 通过基类函数,更新 requested override config
    super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
    mCurrentOverrideConfigurationChanges = 0;
    
    // 配置已经更新完成,重置 mWaitingForConfig
    if (mWaitingForConfig) {
        mWaitingForConfig = false;
        mWmService.mLastFinishedFreezeSource = "new-config";
    }
    
    // 添加 layout reason
    // aply WCT 结束时的 continue layout 会发起窗口刷新
    mAtmService.addWindowLayoutReasons(
            ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
}

所谓的应用新方向,是为了把屏幕的方向等数据,发到底层 SurfaceFlinger,但是用的是 DisplayContent sync transaction 的 setDisplayProjection 方式

// DisplayContent.java

private void applyRotationAndFinishFixedRotation(int oldRotation, int newRotation) {
    
    // null
    final WindowToken rotatedLaunchingApp = mFixedRotationLaunchingApp;
    if (rotatedLaunchingApp == null) {
        applyRotation(oldRotation, newRotation);
        return;
    }

    // ...
}

private void applyRotation(final int oldRotation, final int rotation) {
    // ...    

    //  DisplayContent 的 sync transcation
    final Transaction transaction =
            shellTransitions ? getSyncTransaction() : getPendingTransaction();
 

    // 前面在计算新的 global config 时,调用过这个函数
    // 它主要是根据新旋转方向,更新 DisplayContent#mDisplayInfo,并且把它更到到 DMS
    updateDisplayAndOrientation(null /* outConfig */);

    // ...

    // 立即通知 DMS 重新配置屏幕数据,其中一个数据就是屏幕旋转方向
    // 这些数据会以 transaction 的 setDisplayProjection 进行保存
    // 当 start transcation apply 时,屏幕的新旋转方向就会发送给 SurfaceFlinger
    mWmService.mDisplayManagerInternal.performTraversal(transaction);
    
    
    if (shellTransitions) {
        // Before setDisplayProjection is applied by the start transaction of transition,
        // set the transform hint to avoid using surface in old rotation.
        getPendingTransaction().setFixedTransformHint(mSurfaceControl, rotation);
        // The sync transaction should already contains setDisplayProjection, so unset the
        // hint to restore the natural state when the transaction is applied.
        transaction.unsetFixedTransformHint(mSurfaceControl);
    }

    // ...
}

继续看 DisplayContent override config 更新

// WindowContainer.java

public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
    // We must diff before the configuration is applied so that we can capture the change
    // against the existing bounds.
    // 旋转方向的改变,会造成 bounds change
    final int diff = diffRequestedOverrideBounds(
            overrideConfiguration.windowConfiguration.getBounds());

    // 1. 通过基类 ConfigurationContainer 函数,继续更新 override config
    super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
    
    if (mParent != null) {
        // DisplayContent 有实现此函数,它会发起窗口刷新
        mParent.onDescendantOverrideConfigurationChanged();
    }
    
    // ...
    
    if ((diff & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
        // DisplayContent 下的所有窗口需要 resize
        // 最终结果是把 DisplayContent 下的所有 WindowState 保存到 resizing list 中
        onResize();
    } else {
        // ...
    }
}
// ConfigurationContainer.java

// overrideConfiguration 是 RWC 的 full config
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
    // 1. mRequestedOverrideConfiguration 保存参数 override config
    updateRequestedOverrideConfiguration(overrideConfiguration);
    // Update full configuration of this container and all its children.
    final ConfigurationContainer parent = getParent();

    // 2. 更新 DisplayContent 配置
    // 注意,参数是 RWC 的 full config
    // DisplayContent 有实现这个函数
    onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
}

void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) {
    mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
    // mRequestedOverrideConfiguration 保存 requested override config
    mRequestedOverrideConfiguration.setTo(overrideConfiguration);
    final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds();
    if (mHasOverrideConfiguration && providesMaxBounds()
            && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
        mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
    }
}
// DisplayContent.java

public void onConfigurationChanged(Configuration newParentConfig) {
    final int lastOrientation = getConfiguration().orientation;
    // 通过基类 DisplayArea 处理配置更新
    super.onConfigurationChanged(newParentConfig);
    
    // ...
}
// DisplayArea.java

public void onConfigurationChanged(Configuration newParentConfig) {
    // 收集 DisplayArea(实际为 DisplayContent) 可见性改变
    mTransitionController.collectForDisplayAreaChange(this);
    mTmpConfiguration.setTo(getConfiguration());
    
    // 通过基类 WindowContainer 的函数,更新配置
    // 最终还是通过基类 ConfigurationContainer 标准的配置更新函数,完成 DisplayContent 配置更新
    super.onConfigurationChanged(newParentConfig);

    // ...
}

DisplayContent 最终还是通过基类 ConfigurationContainer 标准的配置更新函数完成配置更新,最终更新所有 children 的配置,这里就不再深究流程。接下来主要看如何收集 DisplayArea(实际为 DisplayContent) 可见性改变

// TransitionController.java

// wc 此时是 DisplayContent
void collectForDisplayAreaChange(@NonNull DisplayArea<?> wc) {
    // Transition 有收集过 DisplayContent
    final Transition transition = mCollectingTransition;
    if (transition == null || !transition.mParticipants.contains(wc)) return;
    
    // 1. 为 DisplayContent 创建截图层
    transition.collectVisibleChange(wc);
    
    // 2. Transition 收集 DisplayContent 下所有可见的 task
    // Collect all visible tasks.
    wc.forAllLeafTasks(task -> {
        if (task.isVisible()) {
            transition.collect(task);
        }
    }, true /* traverseTopToBottom */);
    
    // 3. Transition 收集DisplayContent下所有非 app 窗口
    // Collect all visible non-app windows which need to be drawn before the animation starts.
    final DisplayContent dc = wc.asDisplayContent();
    if (dc != null) {
        final boolean noAsyncRotation = dc.getAsyncRotationController() == null;
        wc.forAllWindows(w -> {
            if (w.mActivityRecord == null && w.isVisible() && !isCollecting(w.mToken)
                    && (noAsyncRotation || !AsyncRotationController.canBeAsync(w.mToken))) {
                transition.collect(w.mToken);
            }
        }, true /* traverseTopToBottom */);
    }
}

Transition 收集 WC 就略过,这里主要看如何为 DisplayContent 创建一个截图层,并显示在最上层

// Transition.java

/**
 * Records that a particular container is changing visibly (ie. something about it is changing
 * while it remains visible). This only effects windows that are already in the collecting
 * transition.
 */
// 参数 wc 是 DisplayContent
void collectVisibleChange(WindowContainer wc) {
    // ...

    if (mContainerFreezer == null) {
        mContainerFreezer = new ScreenshotFreezer();
    }
    
    // ...
    
    // 冻结 wc,其实就是为 wc 创建并显示一个截图层
    mContainerFreezer.freeze(wc, change.mAbsoluteBounds);
}
// Transition.java

private class ScreenshotFreezer implements IContainerFreezer {
    private final ArraySet<WindowContainer> mFrozen = new ArraySet<>();

    /** Takes a screenshot and puts it at the top of the container's surface. */
    @Override
    public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
        // ...

        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Screenshotting %s [%s]",
                wc.toString(), bounds.toString());

        // 参数 bounds 是 DisplayContent full config  Bound
        Rect cropBounds = new Rect(bounds);
        cropBounds.offsetTo(0, 0);
        
        // true
        final boolean isDisplayRotation = wc.asDisplayContent() != null
                && wc.asDisplayContent().isRotationChanging();
        
        // 1. 创建截图层的 buffer
        ScreenCapture.LayerCaptureArgs captureArgs =
                // wc 是 DisplayContent
                new ScreenCapture.LayerCaptureArgs.Builder(wc.getSurfaceControl())
                        .setSourceCrop(cropBounds)
                        .setCaptureSecureLayers(true)
                        .setAllowProtected(true)
                        .setHintForSeamlessTransition(isDisplayRotation)
                        .build();
        ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
                ScreenCapture.captureLayers(captureArgs);
        final HardwareBuffer buffer = screenshotBuffer == null ? null
                : screenshotBuffer.getHardwareBuffer();
        
        // ...
        
        // 2. 创建截图层 surface
        // 截图层的名字叫 RotationLayer
        final String name = isDisplayRotation ? "RotationLayer" : "transition snapshot: " + wc;
        SurfaceControl snapshotSurface = wc.makeAnimationLeash()
                .setName(name)
                .setOpaque(wc.fillsParent())
                .setParent(wc.getSurfaceControl())
                .setSecure(screenshotBuffer.containsSecureLayers())
                .setCallsite("Transition.ScreenshotSync")
                .setBLASTLayer()
                .build();
        
        // 保存冻结的 WC,即 DisplayContent
        mFrozen.add(wc);
        
        // 3. DisplayContent 的 ChangeInfo 中保存截图 surface
        final ChangeInfo changeInfo = Objects.requireNonNull(mChanges.get(wc));
        changeInfo.mSnapshot = snapshotSurface;
        
        // ...
        
        // 4. 在最上层显示截图层 surface
        SurfaceControl.Transaction t = wc.mWmService.mTransactionFactory.get();
        TransitionAnimation.configureScreenshotLayer(t, snapshotSurface, screenshotBuffer);
        t.show(snapshotSurface);
        t.setLayer(snapshotSurface, Integer.MAX_VALUE);
        t.apply(); 
        t.close();
        buffer.close();

        // TODO: 这个操作对于屏幕旋转动画来说,似乎是多余的,但是对其他动画可能有用?
        // Detach the screenshot on the sync transaction (the screenshot is just meant to
        // freeze the window until the sync transaction is applied (with all its other
        // corresponding changes), so this is how we unfreeze it.
        wc.getSyncTransaction().reparent(snapshotSurface, null /* newParent */);
        return true;
    }
}

Activity 处理配置改变

配置更新完成后,会让 top activity 处理配置改变

// DisplayContent.java

// values 为计算出的新配置
// starting 为 null
// deferResume 为 false
boolean updateDisplayOverrideConfigurationLocked(Configuration values,
        ActivityRecord starting, boolean deferResume,
        ActivityTaskManagerService.UpdateConfigurationResult result) {

    int changes = 0;
    boolean kept = true;

    mAtmService.deferWindowLayout();
    try {
        if (values != null) {
            if (mDisplayId == DEFAULT_DISPLAY) {
                // Override configuration of the default display duplicates global config, so
                // we're calling global config update instead for default display. It will also
                // apply the correct override config.
                // 1.更新默认屏幕配置
                changes = mAtmService.updateGlobalConfigurationLocked(values,
                        false /* initLocale */, false /* persistent */,
                        UserHandle.USER_NULL /* userId */);
            } else {
                // ... 
            }
        }

        if (!deferResume) {
            // 2. top activity 处理配置改变
            // 由于 apply WCT 推迟了可见性更新,因此这里不会执行可见性更新
            kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
        }
    } finally {
        mAtmService.continueWindowLayout();
    }

    // ...
    return kept;
}
// ActivityTaskManagerService.java

// starting 为 null
// changes 为 CONFIG_WINDOW_CONFIGURATION|CONFIG_SCREEN_SIZE|CONFIG_ORIENTATION
boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
    boolean kept = true;
    final Task mainRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
    if (mainRootTask != null) {
        if (changes != 0 && starting == null) {
            // 获取 top focused root task 下的 top non-finishing activity
            starting = mainRootTask.topRunningActivity();
        }

        if (starting != null) {
            // top activity 处理配置改变
            kept = starting.ensureActivityConfiguration(changes,
                    false /* preserveWindow */);
            
            // 这里不会更新 activity 可见性,因为 apply WCT 时,推迟了可见性更新
            mRootWindowContainer.ensureActivitiesVisible(starting, changes,
                    !PRESERVE_WINDOWS);
        }
    }

    return kept;
}
// ActivityRecord.java

boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
    return ensureActivityConfiguration(globalChanges, preserveWindow,
            false /* ignoreVisibility */, false /* isRequestedOrientationChanged */);
}

// globalChanges 值为 CONFIG_WINDOW_CONFIGURATION | CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
// 其他都为 false
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
        boolean ignoreVisibility, boolean isRequestedOrientationChanged) {
    final Task rootTask = getRootTask();
    
    //  ...

    ProtoLog.v(WM_DEBUG_CONFIGURATION, "Ensuring correct "
            + "configuration: %s", this);

    // ... 


    // 保存上次发送给 app 的 merged override config
    mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
    
    // ...

    // Okay we now are going to make this activity have the new config.
    // But then we need to figure out how it needs to deal with that.

    // Find changes between last reported merged configuration and the current one. This is used
    // to decide whether to relaunch an activity or just report a configuration change.
    // 1. 检测 ActivityRecord full config 与 last reported merged configuration 差异
    // changes 代表需要 Actvity 处理的配置改变
    // 通过下面的 log 可知 changes 值为 CONFIG_ORIENTATION
    final int changes = getConfigurationChanges(mTmpConfig);

    final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
    // 更新 mLastReportedConfiguration 的 mGlobalConfig、mOverrideConfig
    setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);

    // ...

    // 这里 log 记录了需要 Activity 处理那些配置改变
    ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration changes for %s, "
            + "allChanges=%s", this, Configuration.configurationDiffToString(changes));

    // ...

    // 这个log也很有用,它对比当前配置改变与Actvity能处理的配置改变,这就决定了 Activity 是否要 relaunch
    // info.getRealConfigChanged() 代表 Activity 声明的能处理的配置改变
    // Figure out how to handle the changes between the configurations.
    ProtoLog.v(WM_DEBUG_CONFIGURATION, "Checking to restart %s: changed=0x%s, "
            + "handles=0x%s, mLastReportedConfiguration=%s", info.name,
            Integer.toHexString(changes), Integer.toHexString(info.getRealConfigChanged()),
            mLastReportedConfiguration);

    // 2. 检测 activity 是否需要 relaunch
    // 原来很简单, 如果 Activity 声明的能处理的配置改变,不包含当前需要处理的配置改变,那么就要 relaunch
    boolean shouldRelaunchLocked = shouldRelaunchLocked(changes, mTmpConfig);

    // ...

    if (shouldRelaunchLocked || forceNewConfig) {
        // Aha, the activity isn't handling the change, so DIE DIE DIE.
        configChangeFlags |= changes;
        
        // ...
        
        // preserveWindow 初始为 false,那么这里还是为 false
        // Do not preserve window if it is freezing screen because the original window won't be
        // able to update drawn state that causes freeze timeout.
        preserveWindow &= isResizeOnlyChange(changes) && !mFreezingScreen;
        
        // ...
        
        if (mState == PAUSING) {

        } else {
            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Config is relaunching %s",
                    this);
            if (!mVisibleRequested) {
            }
            
            // 3. relaunch activity
            relaunchActivityLocked(preserveWindow);
        }

        // All done...  tell the caller we weren't able to keep this activity around.
        return false;
    }
    

    // Default case: the activity can handle this new configuration, so hand it over.
    // NOTE: We only forward the override configuration as the system level configuration
    // changes is always sent to all processes when they happen so it can just use whatever
    // system level configuration it last got.
    if (displayChanged) {
        
    } else {
        // Activity 能处理配置改变,就直接通知Activity配置更新,即调用 Activity#onConfigurationChanged()
        // 本文分析的案例,不走这里
        scheduleConfigurationChanged(newMergedOverrideConfig);
    }
  

    // ...
}

本文使用的案例的 MainActivity 并没有在 AndroidManifest 中声明可以处理哪些配置改变,因此要执行 Activity relaunch 流程,即通知 app 端 relaunch activity

// ActivityRecord.java

// preserveWindow 为 false
void relaunchActivityLocked(boolean preserveWindow) {
    // ...

    // true
    final boolean andResume = shouldBeResumed(null /*activeActivity*/);
    
    // ...
    
    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
            "Relaunching: " + this + " with results=" + pendingResults
                    + " newIntents=" + pendingNewIntents + " andResume=" + andResume
                    + " preserveWindow=" + preserveWindow);
    
    if (andResume) {
        // event log 表明 activity 发生 relaunch
        EventLogTags.writeWmRelaunchResumeActivity(mUserId, System.identityHashCode(this),
                task.mTaskId, shortComponentName, Integer.toHexString(configChangeFlags));
    } else {
        // ...
    }
    
    // ...
    
    try {
        ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
                (andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
        
        forceNewConfig = false;
        
        // ActivityRecord#mPendingRelaunchCount + 1, 并且清除 ActivityRecord all drawn 状态
        startRelaunching();
        
        // 通知 app 端 relaunch activity
        final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
                pendingNewIntents, configChangeFlags,
                new MergedConfiguration(getProcessGlobalConfiguration(),
                        getMergedOverrideConfiguration()),
                preserveWindow);
        final ActivityLifecycleItem lifecycleItem;
        if (andResume) { // 走这里
            // reluanch 后需要 resume,那么生命周期执行到 onResumed()
            lifecycleItem = ResumeActivityItem.obtain(isTransitionForward(),
                    shouldSendCompatFakeFocus());
        } else {
            // 从这里可以看出,relaunch activity 并不一定要把生命后期执行到 onResume()
            lifecycleItem = PauseActivityItem.obtain();
        }
        final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), token);
        transaction.addCallback(callbackItem);
        transaction.setLifecycleStateRequest(lifecycleItem);
        mAtmService.getLifecycleManager().scheduleTransaction(transaction);
    } catch (RemoteException e) {}
    
    // ...
}

配置更新时序图

sequenceDiagram
Transition ->> DisplayContent : sendNewConfiguration
DisplayContent ->> DisplayContent : computeScreenConfiguration 计算新配置
DisplayContent ->> ActivityTaskManagerService : updateGlobalConfigurationLocked
ActivityTaskManagerService ->>ActivityTaskManagerService: 使用 RWC full config 计算新的 global config
ActivityTaskManagerService ->> RootWindowContainer : onConfigurationChanged 发送新 global config 给 RWC
RootWindowContainer ->> DisplayContent : performDisplayOverrideConfigUpdate
activate DisplayContent
DisplayContent ->> DisplayContent: onRequestedOverrideConfigurationChanged 
activate DisplayContent
DisplayContent ->> DisplayContent:applyRotationAndFinishFixedRotation
DisplayContent ->> WindowContainer : onRequestedOverrideConfigurationChanged
deactivate DisplayContent
deactivate DisplayContent
WindowContainer ->> ConfigurationContainer : onRequestedOverrideConfigurationChanged
activate ConfigurationContainer
ConfigurationContainer ->> ConfigurationContainer : updateRequestedOverrideConfiguration
ConfigurationContainer ->> RootWindowContainer : onConfigurationChanged
deactivate ConfigurationContainer
RootWindowContainer ->> DisplayArea : onConfigurationChanged
activate DisplayArea
DisplayArea ->> TransitionController : collectForDisplayAreaChange
note over DisplayArea, TransitionController : 创建截图层、收集可见 Task
DisplayArea ->> WindowContainer : onConfigurationChanged
deactivate  DisplayArea
WindowContainer ->> ConfigurationContainer : onConfigurationChanged
note right of ConfigurationContainer:DisplayContent把配置发给所有children
DisplayContent ->> ActivityTaskManagerService : ensureConfigAndVisibilityAfterUpdate