Android T 禁止应用添加窗口的操作

什么情况下会出现我们需要禁止应用添加窗口的情况呢?

假如有一个应用的窗口,我们点开后是透明的或者会影响到系统的使用,那么我们就有必要对这个窗口操作一下

回顾我们在Android T WMS窗口相关流程中所讲的内容

禁止应用添加窗口的操作有两种

1.直接在客户端对应用禁止添加窗口

2.在服务端禁止应用添加窗口

客户端对应用禁止添加窗口

一般来说,应用添加窗口的方式是通过addView()方法直接添加,我们也只需在这个里面修改即可,参考修改如下:

代码路径:frameworks/base/core/java/android/view/WindowManagerGlobal.java

java 复制代码
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
        
        /* modify TAG START */
        if (((WindowManager.LayoutParams) params).type == WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        && "问题窗口包名".equals(ActivityThread.currentPackageName())) {
            android.util.Log.e("TEST","问题窗口包名    有毛病,我不想添加它");
            return;
        }
        /* modify TAG END */

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

可以通过dump window来查看的窗口类型,这里我们以TYPE_APPLICATION_OVERLAY为例,其表示系统覆盖窗口在所有应用窗口上方,但在状态栏和输入法窗口下方。

此时的windowState是还没创建的,无法通过windowState的mAttrs属性获取包名,因此使用ActivityThread.currentPackageName()获取当前执行的进程的包名

在服务端禁止应用添加窗口

我们知道服务端添加窗口的方法就是WindowManagerService中的addWindow()方法,这个方法里面会对需要添加的窗口先进行验证

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

java 复制代码
    public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
            ......
            res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
            if (res != ADD_OKAY) {
                return res;
            }
            ......
    }

displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid)调用DisplayPolicy.java的validateAddingWindowLw()方法,该方法会对窗口TYPE,FLAG等多方面判断。只有返回ADD_OKAY时表示允许当前窗口的添加,反之则不允许添加该窗口。

在WindowManagerGlobal.java中有定义这些返回值

java 复制代码
    public static final int ADD_OKAY = 0;
    public static final int ADD_BAD_APP_TOKEN = -1;
    public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
    public static final int ADD_NOT_APP_TOKEN = -3;
    public static final int ADD_APP_EXITING = -4;
    public static final int ADD_DUPLICATE_ADD = -5;
    public static final int ADD_STARTING_NOT_NEEDED = -6;
    public static final int ADD_MULTIPLE_SINGLETON = -7;
    public static final int ADD_PERMISSION_DENIED = -8;
    public static final int ADD_INVALID_DISPLAY = -9;
    public static final int ADD_INVALID_TYPE = -10;
    public static final int ADD_INVALID_USER = -11;

返回的res最终会走到ViewRootImpl的setView方法中

代码路径:frameworks/base/core/java/android/view/ViewRootImpl.java

java 复制代码
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                ......
                if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                if (res < WindowManagerGlobal.ADD_OKAY) {
                    mAttachInfo.mRootView = null;
                    mAdded = false;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    switch (res) {
                        case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                        case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not valid; is your activity running?");
                        case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not for an application");
                        case WindowManagerGlobal.ADD_APP_EXITING:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- app for token " + attrs.token
                                    + " is exiting");
                        case WindowManagerGlobal.ADD_DUPLICATE_ADD:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- window " + mWindow
                                    + " has already been added");
                        case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
                            // Silently ignore -- we would have just removed it
                            // right away, anyway.
                            return;
                        case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
                            throw new WindowManager.BadTokenException("Unable to add window "
                                    + mWindow + " -- another window of type "
                                    + mWindowAttributes.type + " already exists");
                        case WindowManagerGlobal.ADD_PERMISSION_DENIED:
                            throw new WindowManager.BadTokenException("Unable to add window "
                                    + mWindow + " -- permission denied for window type "
                                    + mWindowAttributes.type);
                        case WindowManagerGlobal.ADD_INVALID_DISPLAY:
                            throw new WindowManager.InvalidDisplayException("Unable to add window "
                                    + mWindow + " -- the specified display can not be found");
                        case WindowManagerGlobal.ADD_INVALID_TYPE:
                            throw new WindowManager.InvalidDisplayException("Unable to add window "
                                    + mWindow + " -- the specified window type "
                                    + mWindowAttributes.type + " is not valid");
                        case WindowManagerGlobal.ADD_INVALID_USER:
                            throw new WindowManager.BadTokenException("Unable to add Window "
                                    + mWindow + " -- requested userId is not valid");
                    }
                    throw new RuntimeException(
                            "Unable to add window -- unknown error code " + res);
                }

                ......
            }
        }
    }

这里满足if (res < WindowManagerGlobal.ADD_OKAY) 才会进入后面的switch (res)

因此我们在服务端修改代码有三步:

1.在WindowManagerGlobal中添加返回值常量,该值小于ADD_OKAY,也就是小于0即可,例如:
public static final int ADD_FORBID = -99;

2.在ViewRootImpl的setView方法中switch (res)添加相应的 case,例如:

java 复制代码
switch (res) {
	......
	case WindowManagerGlobal.ADD_FORBID:
		android.util.Log.e("ViewRootImpl.setView","问题窗口包名    有毛病,我不想添加它");
		return;
	}

3.DisplayPolicy.java的validateAddingWindowLw()方法中进行过滤窗口操作,例如

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

java 复制代码
    /**
     * Check if a window can be added to the system.
     *
     * Currently enforces that two window types are singletons per display:
     * <ul>
     * <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li>
     * <li>{@link WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}</li>
     * <li>{@link WindowManager.LayoutParams#TYPE_NAVIGATION_BAR}</li>
     * </ul>
     *
     * @param attrs Information about the window to be added.
     *
     * @return If ok, WindowManagerImpl.ADD_OKAY.  If too many singletons,
     * WindowManagerImpl.ADD_MULTIPLE_SINGLETON
     */
    int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int callingUid) {
        ......
                /* modify TAG START */
        if (attrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY && "问题窗口包名".equals(attrs.packageName) {
            android.util.Log.e("DisplayPolicy.validateAddingWindowLw","问题窗口包名    有毛病,我不想添加它");
            return ADD_FORBID;
        }
        /* modify TAG END */
        return ADD_OKAY;
    }

此时的windowState已经创建了,所有我们直接通过attrs.packageName获取应用包名即可。

相关推荐
曾经的三心草2 小时前
Mysql之约束与事件
android·数据库·mysql·事件·约束
guoruijun_2012_46 小时前
fastadmin多个表crud连表操作步骤
android·java·开发语言
Winston Wood6 小时前
一文了解Android中的AudioFlinger
android·音频
B.-8 小时前
Flutter 应用在真机上调试的流程
android·flutter·ios·xcode·android-studio
有趣的杰克8 小时前
Flutter【04】高性能表单架构设计
android·flutter·dart
大耳猫13 小时前
主动测量View的宽高
android·ui
帅次16 小时前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
枯骨成佛17 小时前
Android中Crash Debug技巧
android
kim56591 天前
android studio 更改gradle版本方法(备忘)
android·ide·gradle·android studio
咸芝麻鱼1 天前
Android Studio | 最新版本配置要求高,JDK运行环境不适配,导致无法启动App
android·ide·android studio