系统裁剪–移除系统弹窗通知

前一阵子被拉去评估一个需求,有个物联网产品是建立在Android平台基础上的,Android中的一些弹窗提示不需要,客户说一定要移除掉。

首先有哪些需要去掉呢?Android常见的提示控件:

  • Dialog
  • PopWindow
  • Toast

Dialog 都是弹出性提示,弹一个窗口,像长按电源键关机重启选项,音量大小调节弹窗啊,低电电量提示都是用的这个控件,其中客户要求有些系统的提示还是需要保留的。
PopWindow 系统使用的不多,一般输入法弹出方式就是用的PopWindow,还有Spinner的选择弹窗也是使用的PopWindow
Toast 系统和应用基本都会用到的,没什么好介绍的。

一个一个弹窗找肯定是不现实的,我们直接从源头上解决。
首先是Dialog和Toast控件,这两个控件在显示时都需要调用.show()方法,我们在这里做文章:
1.在show方法里可以获取当前的调用包名,然后匹配一个白名单,客户应用可以放到这个白名单里,允许弹窗的可以让他弹,不允许的直接return
2.对于同一个应用包名中某些弹窗需要弹,某些弹窗不需要的,增加setForceShow方法,在需要显示弹窗调用的地方使用这个方法强制弹窗
代码逻辑大致如下:

private boolean mForceShow;public void setForceShow(boolean forceShow){mForceShow = forceShow;
}public void show() {String pkg = mContext.getPackageName();if(!pkg.equals("com.xxx.xxx") && //客户应用包名!pkg.equals("com.android.inputmethod.latin") && //输入法包名!pkg.equals("com.android.settings") && //Settings包名!mForceShow){ //如果设置了mForceShow=true,就强制显示弹窗return;}......
}

因为新加入一个setForceShow方法,原生API是没有的,这里需要将编译出的framework.jar导入到Android Studio中,否则AS在编译的时候会报找不到方法

PopWindow较为特殊,它触发显示的方法是invokePopup(),代码修改呢也是差不多的。

这时客户又报了新的需求,客户有个输入用户名界面,使用的常见的Edittext控件,他需要这个控件长安选中文字的时候不弹出Copy、Share、SelectAll等提示
这个其实是系统的剪切板功能,客户说不需要这个,OK其实这个也好修改,直接上代码:
frameworks/base/core/java/android/widget/Editor.java

/*** @return True if this view supports insertion handles.*/
boolean hasInsertionController() {return mInsertionControllerEnabled;
}
/*** @return True if this view supports selection handles.*/
boolean hasSelectionController() {return mSelectionControllerEnabled;
}

让这两个方法返回false就行了

此时客户又追加需求,在他的用户协议页面长按文字时会弹出一个拷贝分享的弹窗,他也不想要这个功能,而他的用户协议使用chromium-WebView界面显示的,然后客户应用包名又必须加白名单里,上面的setForceShow这个他们又嫌麻烦,系统这边很难进行修改,咱又没有客户源码,有咱也不敢改啊

怎么办呢?

通过打堆栈的方式发现,这个弹窗其实最后调用的也是PopupWindow,继续在invokePopup方法中添加逻辑判断了,但是invokePopup中不能直接毙客户应用包名,这时我想到能否通过判断invokePopup的调用堆栈来判断,首先先看一下堆栈:
这个堆栈中能看到org.chromium.android_webview和org.chromium.content.browser等字样

04-19 01:54:32.271  3757  3757 W System.err: java.lang.Exception: xxx
04-19 01:54:32.271  3757  3757 W System.err:    at android.widget.PopupWindow.invokePopup(PopupWindow.java:1575)
04-19 01:54:32.271  3757  3757 W System.err:    at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1347)
04-19 01:54:32.271  3757  3757 W System.err:    at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1313)
04-19 01:54:32.271  3757  3757 W System.err:    at org.chromium.android_webview.PopupTouchHandleDrawable.show(chromium-SystemWebView.apk-default-495156103:562)
04-19 01:54:32.271  3757  3757 W System.err:    at android.os.MessageQueue.nativePollOnce(Native Method)
04-19 01:54:32.271  3757  3757 W System.err:    at android.os.MessageQueue.next(MessageQueue.java:335)
04-19 01:54:32.271  3757  3757 W System.err:    at android.os.Looper.loopOnce(Looper.java:161)
04-19 01:54:32.271  3757  3757 W System.err:    at android.os.Looper.loop(Looper.java:288)
04-19 01:54:32.271  3757  3757 W System.err:    at android.app.ActivityThread.main(ActivityThread.java:7918)
04-19 01:54:32.271  3757  3757 W System.err:    at java.lang.reflect.Method.invoke(Native Method)
04-19 01:54:32.271  3757  3757 W System.err:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
04-19 01:54:32.271  3757  3757 W System.err:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:942)
04-19 01:54:32.349  3757  3757 W System.err: java.lang.Exception: xxx
04-19 01:54:32.350  3757  3757 W System.err:    at android.widget.PopupWindow.invokePopup(PopupWindow.java:1575)
04-19 01:54:32.350  3757  3757 W System.err:    at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1347)
04-19 01:54:32.350  3757  3757 W System.err:    at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1313)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.widget.floatingtoolbar.LocalFloatingToolbarPopup.show(LocalFloatingToolbarPopup.java:371)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.widget.floatingtoolbar.LocalFloatingToolbarPopup.show(LocalFloatingToolbarPopup.java:346)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.widget.floatingtoolbar.FloatingToolbar.doShow(FloatingToolbar.java:227)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.widget.floatingtoolbar.FloatingToolbar.show(FloatingToolbar.java:167)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.view.FloatingActionMode$FloatingToolbarVisibilityHelper.updateToolbarVisibility(FloatingActionMode.java:386)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.view.FloatingActionMode.repositionToolbar(FloatingActionMode.java:217)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.view.FloatingActionMode.updateViewLocationInWindow(FloatingActionMode.java:167)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.view.FloatingActionMode.invalidateContentRect(FloatingActionMode.java:151)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.view.FloatingActionMode.invalidate(FloatingActionMode.java:145)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.policy.DecorView.setHandledFloatingActionMode(DecorView.java:2101)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.policy.DecorView.setHandledActionMode(DecorView.java:1941)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.policy.DecorView.startActionMode(DecorView.java:929)
04-19 01:54:32.350  3757  3757 W System.err:    at com.android.internal.policy.DecorView.startActionModeForChild(DecorView.java:884)
04-19 01:54:32.350  3757  3757 W System.err:    at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:1036)
04-19 01:54:32.350  3757  3757 W System.err:    at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:1036)
04-19 01:54:32.350  3757  3757 W System.err:    at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:1036)
04-19 01:54:32.350  3757  3757 W System.err:    at android.view.View.startActionMode(View.java:7705)
04-19 01:54:32.350  3757  3757 W System.err:    at org.chromium.content.browser.selection.SelectionPopupControllerImpl.w(chromium-SystemWebView.apk-default-495156103:31)
04-19 01:54:32.350  3757  3757 W System.err:    at Rg0.a(chromium-SystemWebView.apk-default-495156103:1605)
04-19 01:54:32.350  3757  3757 W System.err:    at Zj0.i(chromium-SystemWebView.apk-default-495156103:259)
04-19 01:54:32.350  3757  3757 W System.err:    at b8.run(chromium-SystemWebView.apk-default-495156103:454)
04-19 01:54:32.350  3757  3757 W System.err:    at android.os.Handler.handleCallback(Handler.java:942)
04-19 01:54:32.350  3757  3757 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:99)
04-19 01:54:32.350  3757  3757 W System.err:    at android.os.Looper.loopOnce(Looper.java:201)
04-19 01:54:32.350  3757  3757 W System.err:    at android.os.Looper.loop(Looper.java:288)
04-19 01:54:32.350  3757  3757 W System.err:    at android.app.ActivityThread.main(ActivityThread.java:7918)
04-19 01:54:32.351  3757  3757 W System.err:    at java.lang.reflect.Method.invoke(Native Method)
04-19 01:54:32.351  3757  3757 W System.err:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
04-19 01:54:32.351  3757  3757 W System.err:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:942)

而在开发中我们是可以通过Thread.currentThread().getStackTrace()获取当前的调用栈的,你不仁我不义,上奇巧淫记,在invokePopup中添加如下代码

    String pkg = mContext.getPackageName();for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) {String className = stackTraceElement.getClassName();if (pkg.equals("com.xxx") &&  //客户应用包名className != null && (className.contains("org.chromium.android_webview") ||  //判断调用堆栈是否包含chromium-WebView调用的关键classNameclassName.contains("org.chromium.content.browser"))) {return;}}

当然以上方案也是有风险的,比如客户应用中所有使用chromium-WebView弹出提示的都会被屏蔽,客户如果换用其他内核的WebView可能导致className不匹配,功能失效,然而客户并不在乎

又有人问了,你通过getStackTrace方法不怕有什么风险吗?
其实要是仔细分析过源码,你会发现系统源码里很多地方都会使用这个,比如:

frameworks/base/core/java/android/os/Debug.java

/*** Return a string consisting of methods and locations at multiple call stack levels.* @param depth the number of levels to return, starting with the immediate caller.* @return a string describing the call stack.* {@hide}*/
@UnsupportedAppUsage
public static String getCallers(final int depth) {final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();StringBuilder sb = new StringBuilder();for (int i = 0; i < depth; i++) {sb.append(getCaller(callStack, i)).append(" ");}return sb.toString();
}

后续

书接上次的移除系统弹窗的问题,发现了一个BUG,这里记录一下
测试报的现象:机器一定概率出现冻屏问题
好在概率很高,直接本地复现,此时屏幕一片黑,发现屏幕点击确实没反应,确实像是冻屏了,但是电源键可以亮灭屏,并且长按电源键也有关机选项弹窗,初步排除是冻屏问题

先得知道当前显示的界面是啥?
通过命令 adb shell dumpsys window | grep mFocus 看到当前显示的界面是客户应用界面,也就是客户应用可能出问题了
看看客户应用在干嘛。
通过adb shell ps -A | grep 加客户应用包名获取对应的pid(pidof命令也可以)
通过adb shell kill -3 pid 在data/anr生成对应的代码堆栈,间隔30S,再执行下,先看看两次主线程都在干嘛

 "main" prio=5 tid=1 Native| group="main" sCount=1 ucsCount=0 flags=1 obj=0x72b715b0 self=0xb4000073e61e47b0| sysTid=3578 nice=-4 cgrp=top-app sched=0/0 handle=0x758de014f8| state=S schedstat=( 332030820 23553030 494 ) utm=19 stm=13 core=7 HZ=100| stack=0x7feab40000-0x7feab42000 stackSize=8188KB| held mutexes=native: #00 pc 00000000000a5c4c  /apex/com.android.runtime/lib64/bionic/libc.so (__ioctl+12) (BuildId: bf5f1ce73f89cca7d6a062eb7877e86a)native: #01 pc 000000000005caa0  /apex/com.android.runtime/lib64/bionic/libc.so (ioctl+160) (BuildId: bf5f1ce73f89cca7d6a062eb7877e86a)native: #02 pc 000000000005d08c  /system/lib64/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+284) (BuildId: e1935ad90ada7a9797034de30dc0d0da)native: #03 pc 000000000005e2e8  /system/lib64/libbinder.so (android::IPCThreadState::waitForResponse(android::Parcel*, int*)+76) (BuildId: e1935ad90ada7a9797034de30dc0d0da)native: #04 pc 000000000005e024  /system/lib64/libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+224) (BuildId: e1935ad90ada7a9797034de30dc0d0da)native: #05 pc 0000000000055aa0  /system/lib64/libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+192) (BuildId: e1935ad90ada7a9797034de30dc0d0da)native: #06 pc 00000000001769bc  /system/lib64/libandroid_runtime.so (android_os_BinderProxy_transact(_JNIEnv*, _jobject*, int, _jobject*, _jobject*, int)+156) (BuildId: f24f1e660d8558246769c935ddc0acf7)at android.os.BinderProxy.transactNative(Native method)at android.os.BinderProxy.transact(BinderProxy.java:584)at android.app.IActivityManager$Stub$Proxy.handleApplicationCrash(IActivityManager.java:4877)at com.android.internal.os.RuntimeInit$KillApplicationHandler.uncaughtException(RuntimeInit.java:156)at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1073)at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068)at java.lang.Thread.dispatchUncaughtException(Thread.java:2306)

都是在调用handleApplicationCrash方法,这个一般是应用发生崩溃后调用到的,通过IPC调用到system_server进程

同样的方法获取system_server的代码堆栈,搜索handleApplicationCrash看下对应的对端调用

  "binder:1783_B" prio=5 tid=160 Waiting| group="main" sCount=1 ucsCount=0 flags=1 obj=0x140406b0 self=0xb400007c83d26090| sysTid=3501 nice=-4 cgrp=foreground sched=0/0 handle=0x7a5330fcb0| state=S schedstat=( 111610582 132912848 330 ) utm=5 stm=5 core=0 HZ=100| stack=0x7a53218000-0x7a5321a000 stackSize=991KB| held mutexes=at java.lang.Object.wait(Native method)- waiting on <0x064a7e5f> (a com.android.server.am.AppErrorResult)at java.lang.Object.wait(Object.java:442)at java.lang.Object.wait(Object.java:568)at com.android.server.am.AppErrorResult.get(AppErrorResult.java:32)- locked <0x064a7e5f> (a com.android.server.am.AppErrorResult)at com.android.server.am.AppErrors.crashApplicationInner(AppErrors.java:653)at com.android.server.am.AppErrors.crashApplication(AppErrors.java:569)at com.android.server.am.ActivityManagerService.handleApplicationCrashInner(ActivityManagerService.java:8571)at com.android.server.am.ActivityManagerService.handleApplicationCrash(ActivityManagerService.java:8463)at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2086)at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2653)at android.os.Binder.execTransactInternal(Binder.java:1280)at android.os.Binder.execTransact(Binder.java:1244)

AppErrors.crashApplicationInner方法调用到AppErrorResult.get方法

贴下AppErrorResult.java的代码:

final class AppErrorResult {public void set(int res) {synchronized (this) {mHasResult = true;mResult = res;notifyAll();}}public int get() {synchronized (this) {while (!mHasResult) {try {wait();} catch (InterruptedException e) {}}}return mResult;}boolean mHasResult = false;int mResult;
}

对照堆栈可以看到代码停在了get方法里的wait这边,这个wait的逻辑很好理解,只要mHasResult=false就一直wait,而整个代码中只有set方法会去修改这个mHasResult=true,通过添加log,进一步调试发现确实是set方法没有被调用到

在AppErrors中搜索下AppErrorResult.set方法的调用,发现都是在handleShowAppErrorUi方法里,

    services/core/java/com/android/server/am  (3 usages found)AppErrors.java  (3 usages found)handleShowAppErrorUi(Message)  (3 usages found)840 res.set(AppErrorDialog.ALREADY_SHOWING);853 res.set(AppErrorDialog.BACKGROUND_USER);886 res.set(AppErrorDialog.CANT_SHOW);

梳理下应用崩溃时handleShowAppErrorUi的调用逻辑
->ActivityManagerService.handleApplicationCrash
->ActivityManagerService.handleApplicationCrashInner
->AppErrors.crashApplication
->AppErrors.crashApplicationInner
->sendMessage(ActivityManagerService.SHOW_ERROR_UI_MSG)
->AppErrors.handleShowAppErrorUi

crashApplicationInner方法的代码逻辑先是发一个message让UI线程去显示handleShowAppErrorUi,然后就是result.get等弹窗逻辑的返回

void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,int callingPid, int callingUid) {...final Message msg = Message.obtain();msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;taskId = data.taskId;msg.obj = data;mService.mUiHandler.sendMessage(msg);}int res = result.get();       }

这个弹窗逻辑我们是修改过的,回退handleShowAppErrorUi中的修改确实没有这个问题了
之前我们的修改是直接注掉了弹窗显示,导致逻辑到这就断了,稍微修改一下,设置一个AppErrorDialog.CANT_SHOW的返回值

   void handleShowAppErrorUi(Message msg) {if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)&& !crashSilenced && !shouldThottle&& (showFirstCrash || showFirstCrashDevOption || data.repeating)) {//               proc.getDialogController().proc.getDialogController().showCrashDialogs(data);(data);// set result AppErrorDialog.CANT_SHOWif (res != null) {res.set(AppErrorDialog.CANT_SHOW);}if (!proc.isolated) {mProcessCrashShowDialogTimes.put(proc.info.processName, proc.uid, now);}} else {// The device is asleep, so just pretend that the user// saw a crash dialog and hit "force quit".if (res != null) {res.set(AppErrorDialog.CANT_SHOW);}}}}

Android 移除弹窗方案相关推荐

  1. Android 系统(70)---Android刘海屏适配方案

    Android刘海屏适配方案 什么是刘海屏 随着iPhone X发布,国内一些厂商也推出了刘海屏手机,即将发布的Android p也提供了对刘海屏的支持.so,我们的app也要提前做好适配. 屏幕的正 ...

  2. Android网络连接异常处理方案

    Android网络连接异常处理方案 参考文章: (1)Android网络连接异常处理方案 (2)https://www.cnblogs.com/yangcong/p/3523021.html 备忘一下 ...

  3. Android Webview H5 秒开方案实现

    本文首发于微信公众号「玉刚说」 原文链接:Android Webview H5 秒开方案实现 前言 现在许多app都嵌入了H5页面, 然而WebView加载速度慢这个问题却一直影响着用户的体验, 所以 ...

  4. JAndFix: 基于Java实现的Android实时热修复方案

    简述 JAndFix是一种基于Java实现的Android实时热修复方案,它并不需要重新启动就能生效.JAndFix是在AndFix的基础上改进实现,AndFix主要是通过jni实现对method(A ...

  5. Android Art Hook 技术方案

    Android Art Hook 技术方案 by 低端码农 at 2015.4.13 www.im-boy.net 0x1 开始 Anddroid上的ART从5.0之后变成默认的选择,可见ART的重要 ...

  6. android设计安全性,Android操作系统安全性增强方案的设计与实现

    摘要: 随着移动通信技术的发展,移动终端朝着智能化的方向不断迈进,移动互联网业务层出不穷,日益繁荣.但与此同时,移动终端越来越多的涉及商业秘密和个人隐私等敏感信息.移动终端也面临各种安全威胁,如恶意订 ...

  7. 智能会议系统(34)---Android语音通话实现方案及相关技术介绍

    Android语音通话实现方案及相关技术介绍 Android语音通话实现方案及相关技术介绍 语音通话 Step1语音采集和输出 Step2编解码方式 Step3网络传输 Step4去噪声消回音 语音通 ...

  8. 木纹标识lisp_LISP架构中一种新的移动性管理方案研究

    1 前 言 近年来,互联网在基础架构方面暴露出越来越多的问题,BGP路由表的增长问题更是被提入IETF工作日程.根据互联网结构委员会IAB(Internet Architecture Board)的报 ...

  9. android弹出自定义layout菜单,Android:PopWindow — 对Android的底部弹窗、顶部弹窗菜单及自定义界面的使用封装...

    转载请注明出处:http://blog..net/hmyang314/article/details/70613478 PopWindow 对Android的底部弹窗,顶部弹窗菜单及自定义界面的使用封 ...

最新文章

  1. ASP.NET MVC 3 Internationalization
  2. 继承thread类创建线程
  3. Linux 文件与目录基本操作
  4. 44 SD配置-销售凭证设置-定义状态参数文件
  5. 字符编码、常见字符集解析(ASCII、Unicode、UTF-8、GB2312等)
  6. Android笔记 fragment的Tab样式demo
  7. 老题新理解-在话winform之间的窗体传值
  8. python修改pdf文件大小,python读pdf文件字体大小_用Python读取pdf文件
  9. 2017-2018-1 20155213 20155303 实验二 固件程序设计
  10. React/Router
  11. Javascript:使用setAttribute设置某个标签节点display为none仍然显示
  12. 从KRE到XRE:ASP.NET 5中正在消失的那些K
  13. live2d内嵌html,博客(网页)添加 Live2D 看板娘
  14. 统计通话次数和时间的软件_通话时间统计app下载-通话时间统计安卓版 v2.2.2 - 安下载...
  15. 项目管理十大知识领域之项目风险管理
  16. 银行热衷拿区块链专利 背后有何意图
  17. 计算机毕业设计Java-ssm博雅楼自习室预约系统源码+系统+数据库+lw文档
  18. proxychains DNS解析失败问题
  19. 理解 K8S 的设计精髓之 List-Watch机制和Informer模块
  20. 深度学习推荐系统_深度推荐系统

热门文章

  1. js使用ip和子网掩码计算所在网段
  2. python 词云生成鹿鼎记关键词
  3. 7-1 有课几何 (10 分)
  4. 浏览器对url字符的处理
  5. 案例11:高层综合楼防火案例分析(一)
  6. 马里奥(mario)
  7. 网页设计_1·水果沙拉西餐厅网站
  8. cdr文件可以编辑吗 cdr怎么设置不可编辑文件
  9. 北邮计算机学院复试流程2019,2019年北邮网络技术研究院计算机科学与技术考研复试时间复试内容复试流程复试资料及经验...
  10. 利民发布 HR-10 2280 M.2 SSD 散热器,搭载双 AGHP 逆重力热管