最近在做一个播放组件端上防作弊的效果,播放的时候判断广告是否被部分或者完全遮挡了。
比较重要的载体是Window,实际的表现形式是View,所以重点往监控Window和View的创建这样的思路出发。
目前的话一共想到三种方案:
①监控View的创建:
通过给LayoutInflater 设置factory2来感知View的创建:
LayoutInflater.from(context).setFactory2()
当LayoutInflater调用createViewFromTag时:
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
给设置的factory2接口方法onCreateView就能感知到。
缺点是场景有限,只有需要解析xml生成View的时候才能感知到,直接new View就不行了。
②监听Window的创建:
创建Window是这么创建的:
Window w = PolicyManager.makeNewWindow(mContext);
#PolicyManager
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
而这个spolicy是个静态接口对象
private static final IPolicy sPolicy;
实现是com.android.internal.policy.impl.Policy
所以我们可以hook这个sPolicy对象,感知makeNewWindow的时机。
缺点:高版本中不用PolicyManager创建Window,相应的代码处直接变为new PhoneWindow()
③监听WindowManager.addView方法:
WindowManagerService服务在app本地的代理对象为WindowMangerImpl,而里面依赖了WindowManagerGlobal这个对象,它是个单例,但是不是个接口,就不能用动态代理的方式去hook。hook WindowManagerGlobal的好处是兼容性强,这个类从低版本到高版本几乎没修改过。
在WindowManagerGlobal有几个:
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
他们会在调用addView,removeView,updateView时做操作,所以通过hook这些集合也可以感知到addView的出现:写一个继承ArrayList的ObserverList,覆写add的方法,替换回去。
还有一个比较关键的是需要知道加进来这个View在屏幕的位置,通过是
view.getLocationOnScreen(int[])
这个方法可以得到view在屏幕的位置,接着拿到view的宽高判断和我们View的重叠和层级关系。
④跨进程的Dialog,比如crash弹窗:
这个可以根据点击事件的MotionEvent的flag来判断是否有遮挡:
https://stackoverflow.com/questions/44197671/detecting-android-screen-overlays-programmatically