当应用内接入了众多的 SDK,SDK 内部会使用系统服务 NotificationManager 发送通知, 这就导致通知难以管理和控制. 现在我们就用 Hook 技术拦截部分通知, 限制应用内的通知发送操作.
发送通知使用的是 NotificationManager 的 notify 方法, 我们跟随 API 进去看看. 它会使用 INotificationManager 类型的对象, 并调用其 enqueueNotificationWithTag 方法完成通知的发送.
- public void notify(String tag, int id, Notification notification)
- {
- INotificationManager service = getService();
- ...... // 省略部分代码
- try {
- service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
- stripped, idOut, UserHandle.myUserId());
- if (id != idOut[0]) {
- Log.w(TAG, "notify: id corrupted: sent" + id + ", got back" + idOut[0]);
- }
- } catch (RemoteException e) {
- }
- }
- private static INotificationManager sService;
- /** @hide */
- static public INotificationManager getService()
- {
- if (sService != null) {
- return sService;
- }
- IBinder b = ServiceManager.getService("notification");
- sService = INotificationManager.Stub.asInterface(b);
- return sService;
- }
INotificationManager 是跨进程通信的 Binder 类, sService 是 NMS(NotificationManagerService) 在客户端的代理, 发送通知要委托给 sService, 由它传递给 NMS, 具体的原理在这里不再细究, 感兴趣的可以了解系统服务和应用的通信过程.
我们发现 sService 是个静态成员变量, 而且只会初始化一次. 只要把 sService 替换成自定义的不就行了么, 确实如此. 下面用到大量的 Java 反射和动态代理, 特别要注意代码的书写.
- private void hookNotificationManager(Context context) {
- try {
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- // 得到系统的 sService
- Method getService = NotificationManager.class.getDeclaredMethod("getService");
- getService.setAccessible(true);
- final Object sService = getService.invoke(notificationManager);
- Class iNotiMngClz = Class.forName("android.app.INotificationManager");
- // 动态代理 INotificationManager
- Object proxyNotiMng = Proxy.newProxyInstance(getClass().getClassLoader(),
- new Class[]{iNotiMngClz}, new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- log.debug("invoke(). method:{}", method);
- if (args != null && args.length> 0) {
- for (Object arg : args) {
- log.debug("type:{}, arg:{}", arg != null ? arg.getClass() : null, arg);
- }
- }
- // 操作交由 sService 处理, 不拦截通知
- // return method.invoke(sService, args);
- // 拦截通知, 什么也不做
- return null;
- // 或者是根据通知的 Tag 和 ID 进行筛选
- }
- });
- // 替换 sService
- Field sServiceField = NotificationManager.class.getDeclaredField("sService");
- sServiceField.setAccessible(true);
- sServiceField.set(notificationManager, proxyNotiMng);
- } catch (Exception e) {
- log.warn("Hook NotificationManager failed!", e);
- }
- }
Hook 的时机还是尽量要早, 我们在 attachBaseContext 里面操作.
- @Override
- protected void attachBaseContext(Context newBase) {
- super.attachBaseContext(newBase);
- hookNotificationManager(newBase);
- }
这样我们就完成了对通知的拦截, 可见 Hook 技术真的是非常强大, 好多插件化的原理都是建立在 Hook 之上的.
自己是从事了七年开发的 Android 工程师, 不少人私下问我, 2019 年 Android 进阶该怎么学, 方法有没有?
没错, 年初我花了一个多月的时间整理出来的学习资料, 希望能帮助那些想进阶提升 Android 开发, 却又不知道怎么进阶学习的朋友.[包括高级 UI, 性能优化, 架构师课程, NDK,Kotlin, 混合式开发 (ReactNative+Weex),Flutter 等架构技术资料] , 希望能帮助到您面试前的复习且找到一个好的工作, 也节省大家在网上搜索资料的时间来学习.
来源: http://www.jianshu.com/p/a60178c51a7a