序
什么情况下会出现我们需要禁止应用添加窗口的情况呢?
假如有一个应用的窗口,我们点开后是透明的或者会影响到系统的使用,那么我们就有必要对这个窗口操作一下
回顾我们在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
获取应用包名即可。