Android U 自由窗口(浮窗)——操作栏相关

分析思路

本地通过dump activity containers,View树以及window等方式均未找到自由窗口状态栏相关代码,最终通过winscope dumpsys SF找到相关线索。

winscope

通过winscope查看View结构

关键代码

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

java 复制代码
    void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
            RelayoutResult<T> outResult) {
        ......

        // DecorationContainerSurface
        if (mDecorationContainerSurface == null) {
            final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
            mDecorationContainerSurface = builder
                    .setName("Decor container of Task=" + mTaskInfo.taskId)
                    .setContainerLayer()
                    .setParent(mTaskSurface)
                    .build();

            startT.setTrustedOverlay(mDecorationContainerSurface, true)
                    .setLayer(mDecorationContainerSurface,
                            TaskConstants.TASK_CHILD_LAYER_WINDOW_DECORATIONS);
        }

        startT.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight)
                .show(mDecorationContainerSurface);

        // CaptionContainerSurface, CaptionWindowManager
        if (mCaptionContainerSurface == null) {
            final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
            mCaptionContainerSurface = builder
                    .setName("Caption container of Task=" + mTaskInfo.taskId)
                    .setContainerLayer()
                    .setParent(mDecorationContainerSurface)
                    .build();
        }

        final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
        final int captionWidth = taskBounds.width();
        startT.setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight)
                .show(mCaptionContainerSurface);

        if (ViewRootImpl.CAPTION_ON_SHELL) {
            outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);

            // Caption insets
            mCaptionInsetsRect.set(taskBounds);
            mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
            wct.addInsetsSource(mTaskInfo.token,
                    mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
        } else {
            startT.hide(mCaptionContainerSurface);
        }

        // Task surface itself
        float shadowRadius = loadDimension(resources, params.mShadowRadiusId);
        int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
        mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
        mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
        mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
        final Point taskPosition = mTaskInfo.positionInParent;
        startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
                .setShadowRadius(mTaskSurface, shadowRadius)
                .setColor(mTaskSurface, mTmpColor)
                .show(mTaskSurface);
        finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
                .setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
            startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
            finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
        }

        if (mCaptionWindowManager == null) {
            // Put caption under a container surface because ViewRootImpl sets the destination frame
            // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
            mCaptionWindowManager = new WindowlessWindowManager(
                    mTaskInfo.getConfiguration(), mCaptionContainerSurface,
                    null /* hostInputToken */);
        }

        Slog.i("WindowDecoration","mViewHost:"+(mViewHost == null ? "null":mViewHost.toString()),new Exception());
        // Caption view
        mCaptionWindowManager.setConfiguration(taskConfig);
        final WindowManager.LayoutParams lp =
                new WindowManager.LayoutParams(captionWidth, captionHeight,
                        WindowManager.LayoutParams.TYPE_APPLICATION,
                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
        lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
        lp.setTrustedOverlay();
        if (mViewHost == null) {
            mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
                    mCaptionWindowManager);
            if (params.mApplyStartTransactionOnDraw) {
                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
            }
            mViewHost.setView(outResult.mRootView, lp);
        } else {
            if (params.mApplyStartTransactionOnDraw) {
                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
            }
            mViewHost.relayout(lp);
        }
    }

从代码中可以看出"Caption of Task"是一个SurfaceControlViewHost对象,且其在Shell相关代码路径下,说该View是属于SystemUI进程。

如代码中所示,添加log打印堆栈。

堆栈跟踪

这里的流程我们同样以从多任务启动自由窗口为例

java 复制代码
WindowDecoration: mViewHost:null
WindowDecoration: java.lang.Exception
WindowDecoration: 	at com.android.wm.shell.windowdecor.WindowDecoration.relayout(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:282)
WindowDecoration: 	at com.android.wm.shell.windowdecor.CaptionWindowDecoration.relayout(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:120)
WindowDecoration: 	at com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel.createWindowDecoration(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:196)
WindowDecoration: 	at com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel.onTaskChanging(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:128)
WindowDecoration: 	at com.android.wm.shell.freeform.FreeformTaskTransitionObserver.onToFrontTransitionReady(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:147)
WindowDecoration: 	at com.android.wm.shell.freeform.FreeformTaskTransitionObserver.onTransitionReady(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:104)
WindowDecoration: 	at com.android.wm.shell.transition.Transitions.dispatchReady(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:707)
WindowDecoration: 	at com.android.wm.shell.transition.Transitions.onTransitionReady(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:669)
WindowDecoration: 	at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.lambda$onTransitionReady$0(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:1353)
WindowDecoration: 	at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.$r8$lambda$qsRfWn1ItrZqnFeABBdxU50jPc4(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:0)
WindowDecoration: 	at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl$$ExternalSyntheticLambda0.run(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:0)
WindowDecoration: 	at android.os.Handler.handleCallback(Handler.java:958)
WindowDecoration: 	at android.os.Handler.dispatchMessage(Handler.java:99)
WindowDecoration: 	at android.os.Looper.loopOnce(Looper.java:205)
WindowDecoration: 	at android.os.Looper.loop(Looper.java:294)
WindowDecoration: 	at android.os.HandlerThread.run(HandlerThread.java:67)


WindowDecoration: mViewHost:android.view.SurfaceControlViewHost@12f4198
WindowDecoration: java.lang.Exception
WindowDecoration: 	at com.android.wm.shell.windowdecor.WindowDecoration.relayout(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:282)
WindowDecoration: 	at com.android.wm.shell.windowdecor.CaptionWindowDecoration.relayout(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:120)
WindowDecoration: 	at com.android.wm.shell.windowdecor.CaptionWindowDecoration.relayout(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:96)
WindowDecoration: 	at com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel.onTaskInfoChanged(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:108)
WindowDecoration: 	at com.android.wm.shell.freeform.FreeformTaskListener.onTaskInfoChanged(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:136)
WindowDecoration: 	at com.android.wm.shell.ShellTaskOrganizer.onTaskInfoChanged(go/retraceme fde5aedcaed361d3fa72c4a1576906ef6746f16da54ecf293f5080127ba3c502:527)
WindowDecoration: 	at android.window.TaskOrganizer$1.lambda$onTaskInfoChanged$6(TaskOrganizer.java:340)
WindowDecoration: 	at android.window.TaskOrganizer$1.$r8$lambda$FmJPvZyGqAGeVe9o6dSQkNL3f3g(Unknown Source:0)
WindowDecoration: 	at android.window.TaskOrganizer$1$$ExternalSyntheticLambda3.run(Unknown Source:4)
WindowDecoration: 	at android.os.Handler.handleCallback(Handler.java:958)
WindowDecoration: 	at android.os.Handler.dispatchMessage(Handler.java:99)
WindowDecoration: 	at android.os.Looper.loopOnce(Looper.java:205)
WindowDecoration: 	at android.os.Looper.loop(Looper.java:294)
WindowDecoration: 	at android.os.HandlerThread.run(HandlerThread.java:67)

这里我们主要关注mViewHost:null 的这次即可,不为null,则不会进入创建流程。

从堆栈中可以看出这个方法是从TransitionPlayerImpl.onTransitionReady到WindowDecoration.relayout结束

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

java 复制代码
    @BinderThread
    private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
        @Override
        public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
                SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
                throws RemoteException {
            mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
                    iBinder, transitionInfo, t, finishT));
        }
        ......
    }

这里是通过ITransitionPlayer跨进程通信,找到了mTransitionPlayer,在TransitionController中(system_server侧)

搜索其调用 mTransitionPlayer.onTransitionReady(

发现什么都没有,找下mTransitionPlayer相关的get方法

代码路径:frameworks/base/services/core/java/com/android/server/wm/TransitionController.java

java 复制代码
    @Nullable ITransitionPlayer getTransitionPlayer() {
        return mTransitionPlayer;
    }

再次通过getTransitionPlayer().onTransitionReady查找相应调用

最终我们得出TransitionPlayerImpl.onTransitionReady是通过system_server侧Transition.onTransactionReady中mController.getTransitionPlayer().onTransitionReady而来。

可在此处添加堆栈查看system_server侧调用逻辑。

java 复制代码
Transition: getTransitionPlayer().onTransitionReady
Transition: java.lang.Exception
Transition: 	at com.android.server.wm.Transition.onTransactionReady(Transition.java:1575)
Transition: 	at com.android.server.wm.BLASTSyncEngine$SyncGroup.finishNow(BLASTSyncEngine.java:263)
Transition: 	at com.android.server.wm.BLASTSyncEngine$SyncGroup.tryFinish(BLASTSyncEngine.java:202)
Transition: 	at com.android.server.wm.BLASTSyncEngine$SyncGroup.-$$Nest$mtryFinish(BLASTSyncEngine.java:0)
Transition: 	at com.android.server.wm.BLASTSyncEngine.onSurfacePlacement(BLASTSyncEngine.java:552)
Transition: 	at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:810)
Transition: 	at com.android.server.wm.RootWindowContainer.performSurfacePlacement(RootWindowContainer.java:756)
Transition: 	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:177)
Transition: 	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:126)
Transition: 	at com.android.server.wm.WindowManagerService.relayoutWindow(WindowManagerService.java:2410)
Transition: 	at com.android.server.wm.Session.relayout(Session.java:249)
Transition: 	at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:701)
Transition: 	at com.android.server.wm.Session.onTransact(Session.java:178)
Transition: 	at android.os.Binder.execTransactInternal(Binder.java:1344)
Transition: 	at android.os.Binder.execTransact(Binder.java:1275)

这里走了窗口绘制流程,也就是说Caption view的创建是应用onresume流程之后开始的。

Layout Inspector

在android studio中选择tools->Legacy Layout Inspector,找到systemUI进程

其中可以找到对应的caption

选中后我们可以看到其结构

这个里面我们可以看到这些控件的id。

综上所述,android14中,CaptionWindow是systemUI上单独的进程不在应用本身上。

相关推荐
寻星探路19 分钟前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
工程师老罗2 小时前
如何在Android工程中配置NDK版本
android
曹牧3 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
爬山算法3 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty7254 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎4 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄4 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea
忆~遂愿4 小时前
ops-cv 算子库深度解析:面向视觉任务的硬件优化与数据布局(NCHW/NHWC)策略
java·大数据·linux·人工智能
小韩学长yyds4 小时前
Java序列化避坑指南:明确这4种场景,再也不盲目实现Serializable
java·序列化
仟濹4 小时前
【Java基础】多态 | 打卡day2
java·开发语言