记· 当前activity设置防截屏后,通过WindowManager.addview的方式添加的view 仍然可以被录制·之addview流程

问题由来

客户反馈 防截屏功能截屏可以但是录制失效,反编译客户代码发现其被录制的内容均是通过windowManager.addView的形式添加----引申出当前问题:activity设置防截屏后,通过WindowManager.addview的方式添加的view 仍然可以被录制
猜测大概就是addview中的操作所导致了 询问chatGpt 和翻源码做以下分析

问题原因

WindowManager.addView 是添加一个新window,与当前window 不共Flags

ChatGPT 回答

问题原因
解决方案

windowManager.addview源码分析

1. WindowManager.addView 实际是通过WindowManager的实现类执行的 也就是WindowManagerImpl.addView
 public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
 }
//可以看到实际是通过Global调用的addview 传递了view 和params等参数
//Global 就是 WindowManagerGlobal.java
2. mGlobal.addView
 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //
            ViewRootImpl root;
        //
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
				//
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            //
   							//开始绘制view
                root.setView(view, wparams, panelParentView);

    }
//仅保留主要代码
// 可看到创建了viewrootimpl
// 通过viewRootImpl 来绘制view
3. VIewRootImpl.setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
655        synchronized (this) {
656            if (mView == null) {
657                mView = view;
 					         /
  								//设置attachInfo
731                mWindowAttributesChanged = true;
732                mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
733                mAttachInfo.mRootView = view;
734                mAttachInfo.mScalingRequired = mTranslator != null;
735                mAttachInfo.mApplicationScale =
736                        mTranslator == null ? 1.0f : mTranslator.applicationScale;
737                if (panelParentView != null) {
738                    mAttachInfo.mPanelParentWindowToken
739                            = panelParentView.getApplicationWindowToken();
740                }
741                mAdded = true;
742                int res; /* = WindowManagerImpl.ADD_OKAY; */
  								 //请求布局执行 view的绘制流程
747                requestLayout();
748                /
  							//将 Window 添加到屏幕上,mWindowSession 实现了 IWindowSession接口,是 Session 的 Binder 对象。
                // addToDisplay 通知 WindowManagerServer 添加 Window即传递进的view (过AIDL形式)
758               res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
759                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
760                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
761                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
762                
880    }
  // 仅保留主要代码
  //执行 requestLayout();
  //setView函数通知windowManager添加了window
4. mWindowSession.addToDisplay
200    @Override
201    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
202            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
203            Rect outStableInsets, Rect outOutsets,
204            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
205        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
206                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
207    }
//调用WindowManagerService.addWindow()
5. WindowManagerService.addWindow()
public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        int[] appOp = new int[1];
  			// 1. 第一步 检查权限 系统窗口需要对应的系统权限
        int res = mPolicy.checkAddPermission(attrs, appOp);if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }
 
        boolean reportNewConfig = false;
        WindowState parentWindow = null;
        long origId;
        final int callingUid = Binder.getCallingUid();
  			//2. 第二步  检查窗口类型
        final int type = attrs.type; 
 
        synchronized(mWindowMap) {
            if (!mDisplayReady) {
                throw new IllegalStateException("Display has not been initialialized");
            }
	    			//2.1 通过displayId參数指定加入到哪一个DisplayContent,默认是DEFAULT_DISPLAY手机屏幕
          	//一个displayContent 对应一个屏幕绘制 
            final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
            if (displayContent == null) {
                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                        + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            if (!displayContent.hasAccess(session.mUid)
                    && !mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) {
                Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
                        + "does not have access: " + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
 						//2.2 判断是否重复添加Window
            if (mWindowMap.containsKey(client.asBinder())) { 
                Slog.w(TAG_WM, "Window " + client + " is already added");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
 
	    			//2.3 子窗口必须依附于父窗口,对于子窗体来说,attrs.token表示了父窗体
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                parentWindow = windowForClientLocked(null, attrs.token, false);
                if (parentWindow == null) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) { //窗体嵌套只能是两层,子窗口是不能在添加窗口的
                    Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }
 
            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
                Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display.  Aborting.");
                return WindowManagerGlobal.ADD_PERMISSION_DENIED;
            }
 						// 3.检查WindowToken
          	///根据窗口类型找到对应的WindowToken, 如果不是特定的type,且token不存在,则返回。一个WindowToken对应一系列的WindowState, app窗口对应的token为AppWindowToken
            AppWindowToken atoken = null; //应用窗口token
            final boolean hasParent = parentWindow != null;
            // 子窗口使用父窗口策略
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            // If this is a child window, we want to apply the same type checking rules as the
            // parent window type.
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;
 
            boolean addToastWindowRequiresToken = false;
 
            if (token == null) { //token异常判断
               ......
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow); //创建系统WindowToken
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { //应用窗口类型
                atoken = token.asAppWindowToken();
                ......
            } else if (rootType == TYPE_INPUT_METHOD) {
                if (token.windowType != TYPE_INPUT_METHOD) {
                    Slog.w(TAG_WM, "Attempted to add input method window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_VOICE_INTERACTION) {
                if (token.windowType != TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_WALLPAPER) {
                if (token.windowType != TYPE_WALLPAPER) {
                    Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_DREAM) {
                if (token.windowType != TYPE_DREAM) {
                    Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
                if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
                    Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_TOAST) {
                // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
                addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
                        callingUid, parentWindow);
                if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
                    Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_QS_DIALOG) {
                if (token.windowType != TYPE_QS_DIALOG) {
                    Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (token.asAppWindowToken() != null) {
                Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
                // It is not valid to use an app token with other system types; we will
                // instead make a new token for it (as if null had been passed in for the token).
                attrs.token = null;
                token = new WindowToken(this, client.asBinder(), type, false, displayContent,
                        session.mCanAddInternalSystemWindow);//创建系统窗口类型的token
            }
	  			  //4. 第四步 创建WindowState,WMS为要加入的窗体创建了一个WindowState对象,这个对象维护了一个窗体的全部状态信息
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            if (win.mDeathRecipient == null) {
                // Client has apparently died, so there is no reason to
                // continue.
                Slog.w(TAG_WM, "Adding window client " + client.asBinder()
                        + " that is dead, aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }
 
            if (win.getDisplayContent() == null) {
                Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
 
	    			//5.第五步  调整窗口,检查权限
          	//5.1 adjustWindowParamsLw 该函数的调用会调整window LayoutParams的一些成员的属性 比如flags等
          	// mPolicy实现类为,WindowManagerPolicy:
            mPolicy.adjustWindowParamsLw(win.mAttrs); 
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
 						// 5.2 权限检查,根据窗口类型做相关处理
            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }
 
            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
	    ......
 
            win.attach();
	          //6. 将WindowState对象记录到WMS相关变量mWindowMap中,
            mWindowMap.put(client.asBinder(), win);
            if (win.mAppOp != AppOpsManager.OP_NONE) {
                int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
                        win.getOwningPackage());
                if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
                        (startOpResult != AppOpsManager.MODE_DEFAULT)) {
                    win.setAppOpVisibilityLw(false);
                }
            }
 
            final AppWindowToken aToken = token.asAppWindowToken(); //应用窗口windowToken
            if (type == TYPE_APPLICATION_STARTING && aToken != null) {
                aToken.startingWindow = win;
                if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
                        + " startingWindow=" + win);
            }
 
            boolean imMayMove = true;
 						//7. 后面则是对窗口进行处理 
            win.mToken.addWindow(win);
            if (type == TYPE_INPUT_METHOD) {
                win.mGivenInsetsPending = true;
                setInputMethodWindowLocked(win);
                imMayMove = false;
            } else if (type == TYPE_INPUT_METHOD_DIALOG) {
                displayContent.computeImeTarget(true /* updateImeTarget */);
                imMayMove = false;
            } else {
                if (type == TYPE_WALLPAPER) {
                    displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
                    // If there is currently a wallpaper being shown, and
                    // the base layer of the new window is below the current
                    // layer of the target window, then adjust the wallpaper.
                    // This is to avoid a new window being placed between the
                    // wallpaper and its target.
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                }
            }
 
            // If the window is being added to a stack that's currently adjusted for IME,
            // make sure to apply the same adjust to this new window.
            win.applyAdjustForImeIfNeeded();
 
            if (type == TYPE_DOCK_DIVIDER) {
                mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win);
            }
 
            final WindowStateAnimator winAnimator = win.mWinAnimator;
            winAnimator.mEnterAnimationPending = true;
            winAnimator.mEnteringAnimation = true;
            // Check if we need to prepare a transition for replacing window first.
            if (atoken != null && atoken.isVisible()
                    && !prepareWindowReplacementTransition(atoken)) {
                // If not, check if need to set up a dummy transition during display freeze
                // so that the unfreeze wait for the apps to draw. This might be needed if
                // the app is relaunching.
                prepareNoneTransitionForRelaunching(atoken);
            }
 
            if (displayContent.isDefaultDisplay) {
                final DisplayInfo displayInfo = displayContent.getDisplayInfo();
                final Rect taskBounds;
                if (atoken != null && atoken.getTask() != null) {
                    taskBounds = mTmpRect;
                    atoken.getTask().getBounds(mTmpRect);
                } else {
                    taskBounds = null;
                }
                if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayInfo.rotation,
                        displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,
                        outStableInsets, outOutsets)) {
                    res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
                }
            } else {
                outContentInsets.setEmpty();
                outStableInsets.setEmpty();
            }
 
            if (mInTouchMode) {
                res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
            }
            if (win.mAppToken == null || !win.mAppToken.isClientHidden()) { //设置应用窗体可见
                res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
            }
 
            mInputMonitor.setUpdateInputWindowsNeededLw(); //以下和输入法窗体有关
 
            boolean focusChanged = false;
            if (win.canReceiveKeys()) {
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }
 
            if (imMayMove) {
                displayContent.computeImeTarget(true /* updateImeTarget */);
            }
 
            // Don't do layout here, the window must call
            // relayout to be displayed, so we'll do it there.
            displayContent.assignWindowLayers(false /* setLayoutNeeded */);
 
            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);
 
            if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
                    + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
 
            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false, displayId)) {
                reportNewConfig = true;
            }
        }
 
        if (reportNewConfig) {
            sendNewConfiguration(displayId);
        }
 
        Binder.restoreCallingIdentity(origId);
 
        return res;
    }
// 1 检查权限
// 2 检查窗口类型
//3 检查windowtoke
// 4 创建WIndowState 对象维护了一个窗体的全部状态信息
//5 调整窗口属性,检查权限
//6. 将WindowState对象记录到WMS相关变量mWindowMap中,
//7 调整窗口层级以及显示

综上所述

windowManager是 添加了一个新的window ,FLAGS 不与父window 共享。

啊这。这不是一次view添加绘制的过程吗。。。果然源码还是要经常复习 在此感谢扔物线交流群的各位老铁

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值