Android U 多任务启动分屏——SystemUI流程

前文

Android U 多任务启动分屏——Launcher流程(下分屏)
前文说到通过ISplitScreen接口跨进程调用到了SystemUI进程,我们继续分析分屏在systemui中的实现,这些实现都是在WMShell中实现的。

WMShell实现分屏流程

实现ISplitScreen接口

代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java

    /**
     * The interface for calls from outside the host process.
     */
    @BinderThread
    private static class ISplitScreenImpl extends ISplitScreen.Stub
            implements ExternalInterfaceBinder {
        ......
        @Override
        public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
                @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
                @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
            //切换到了binder线程到main线程,调用StageCoordinator的startTasks方法
            executeRemoteCallWithTaskPermission(mController, "startTasks",
                    (controller) -> controller.mStageCoordinator.startTasks(taskId1, options1,
                            taskId2, options2, splitPosition, splitRatio, remoteTransition,
                            instanceId));
        }
        ......
    }

这个方法主要就是做了两件事:
1.权限检查并切换至主线程操作
2.调用StageCoordinator的startTasks方法启动分屏

权限检查并切换至主线程操作

先来说说executeRemoteCallWithTaskPermission方法,这个方法主要是做了一些鉴权和切换至主线程的操作。
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java

/**
 * Helpers for working with executors
 */
public class ExecutorUtils {

    /**
     * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
     * callback.
     */
    public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
            String log, Consumer<T> callback) {
        executeRemoteCallWithTaskPermission(controllerInstance, log, callback,
                false /* blocking */);
    }

    /**
     * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
     * callback.
     */
    public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
            String log, Consumer<T> callback, boolean blocking) {
        if (controllerInstance == null) return;

        final RemoteCallable<T> controller = controllerInstance;
        //检查MANAGE_ACTIVITY_TASKS权限
        controllerInstance.getContext().enforceCallingPermission(
                Manifest.permission.MANAGE_ACTIVITY_TASKS, log);
        if (blocking) {
            try {
                controllerInstance.getRemoteCallExecutor().executeBlocking(() -> {
                    callback.accept((T) controller);
                });
            } catch (InterruptedException e) {
                Slog.e("ExecutorUtils", "Remote call failed", e);
            }
        } else {
            //切换主线程执行callback
            controllerInstance.getRemoteCallExecutor().execute(() -> {
                callback.accept((T) controller);
            });
        }
    }
}

这个方法主要就做了两件事:
1.检查MANAGE_ACTIVITY_TASKS权限
enforceCallingPermission方法的实现在ContextImpl.java中。

2.切换主线程执行callback
这里getRemoteCallExecutor方法的实现就在SplitScreenController.java中。

    @Override
    public ShellExecutor getRemoteCallExecutor() {
        return mMainExecutor;
    }

代码很简单,就是调用主线程。

调用分屏方法

我们再来看看startTasks方法。

controller.mStageCoordinator.startTasks(taskId1, options1,
		taskId2, options2, splitPosition, splitRatio, remoteTransition,
		instanceId));

这里面的参数正是在下分屏的流程中跨进程传递过来的。

分屏方法的实现

设置上分屏

代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java

    /** Starts 2 tasks in one transition. */
    void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
            @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
            @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
        //创建WindowContainerTransaction对象
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        //判断taskId2是否是无效的id,如果是无效id则退出分屏,回到全屏
        if (taskId2 == INVALID_TASK_ID) {
            if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
                prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
            }
            if (mRecentTasks.isPresent()) {
                mRecentTasks.get().removeSplitPair(taskId1);
            }
            options1 = options1 != null ? options1 : new Bundle();
            addActivityOptions(options1, null);
            wct.startTask(taskId1, options1);
            mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
            return;
        }
        
        //设置分屏位置
        setSideStagePosition(splitPosition, wct);
        //获取options1
        options1 = options1 != null ? options1 : new Bundle();
        //设置options1中的token为SideStage
        addActivityOptions(options1, mSideStage);
        //关联options1与taskId1,即taskId1设为SideStage
        wct.startTask(taskId1, options1);
        
        //设置下分屏后,启动分屏
        startWithTask(wct, taskId2, options2, splitRatio, remoteTransition, instanceId);
    }

桌面流程中传递过来的值是0,即splitPosition值为0,因此SideStage在上分屏显示。

设置下分屏

代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java

    /**
     * Starts with the second task to a split pair in one transition.
     *
     * @param wct        transaction to start the first task
     * @param instanceId if {@code null}, will not log. Otherwise it will be used in
     *                   {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
     */
    private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
            @Nullable Bundle mainOptions, float splitRatio,
            @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
        if (!mMainStage.isActive()) {
            // Build a request WCT that will launch both apps such that task 0 is on the main stage
            // while task 1 is on the side stage.
            mMainStage.activate(wct, false /* reparent */);
        }
        //设置分屏比例,桌面侧传递过来的值,默认为0.5
        mSplitLayout.setDivideRatio(splitRatio);
        //计算出上下分屏的bound都设置给对应的configration,
        //传递到systemserver端,方便systemserver更新task的bound
        updateWindowBounds(mSplitLayout, wct);
        //mRootTaskInfo.token指的就是分屏的rootTask
        //让装载分屏的rootTask进行reorder,主要就是为了把分屏移到最前面
        wct.reorder(mRootTaskInfo.token, true);
        //设置rootTask是否半透明
        setRootForceTranslucent(false, wct);

        // Make sure the launch options will put tasks in the corresponding split roots
        //设置下分屏options
        mainOptions = mainOptions != null ? mainOptions : new Bundle();
        addActivityOptions(mainOptions, mMainStage);

        // Add task launch requests
        //添加启动的下分屏task
        wct.startTask(mainTaskId, mainOptions);

        // leave recents animation by re-start pausing tasks
        //清除任务列表中缓存的mainTask
        if (mPausingTasks.contains(mainTaskId)) {
            mPausingTasks.clear();
        }
        //启动进入分屏的过渡动画,提交WindowContainerTransaction,让分屏显示出来
        mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, remoteTransition, this,
                TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
        setEnterInstanceId(instanceId);
    }

我们mSplitTransitions.startEnterTransition这个方法中传递了WindowContainerTransaction对象wct,用于后续system_server流程中提交该事务,并且启动分屏。
其他参数:

  • TRANSIT_TO_FRONT:默认值为3,表示已存在但不可见的窗口将变为可见
    代码路径:frameworks/base/core/java/android/view/WindowManager.java

    /**
     * A window that already existed but was not visible is made visible.
     * @hide
     */
    int TRANSIT_TO_FRONT = 3;
    
  • TRANSIT_SPLIT_SCREEN_PAIR_OPEN:表示同时两个task的动画类型
    代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java

    /** Transition type for launching 2 tasks simultaneously. */
    public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 4;
    

继续跟踪mSplitTransitions.startEnterTransition

启动分屏(过渡至system_server侧处理)

代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java

    /** Starts a transition to enter split with a remote transition animator. */
    IBinder startEnterTransition(
            @WindowManager.TransitionType int transitType,
            WindowContainerTransaction wct,
            @Nullable RemoteTransition remoteTransition,
            Transitions.TransitionHandler handler,
            int extraTransitType, boolean resizeAnim) {
        //mPendingEnter是EnterSession对象,
        //如果已有一个待处理的进入过渡(mPendingEnter 不为 null)表示不会启动新的过渡。
        if (mPendingEnter != null) {
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                    + " skip to start enter split transition since it already exist. ");
            return null;
        }
        //启动动画和提交WindowContainerTransaction相关事务
        final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
        //设置mPendingEnter动画
        //这个方法主要就是创建新的EnterSession,然后复制给mPendingEnter
        setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim);
        return transition;
    }

这里我们主要关注mTransitions.startTransition(transitType, wct, handler);
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java

    /** Start a new transition directly. */
    public IBinder startTransition(@WindowManager.TransitionType int type,
            @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
                + "type=%d wct=%s handler=%s", type, wct, handler);
        final ActiveTransition active = new ActiveTransition();
        active.mHandler = handler;
        //启动新的过渡动画,并提交WindowContainerTransaction相关事务
        active.mToken = mOrganizer.startNewTransition(type, wct);
        mPendingTransitions.add(active);
        return active.mToken;
    }

这个方法用于直接启动一个新的过渡动画,并提交WindowContainerTransaction相关事务。
mOrganizerWindowOrganizer对象,mOrganizer.startNewTransition(type, wct);传递了参数type(TRANSIT_TO_FRONT)和wct(WindowContainerTransaction对象),后续进入到system_server流程中处理。

后续流程

Android U system_server侧WindowContainerTransaction处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值