OPPO R9采用了ColorOS3.0作为手机系统,为了给用户一个良好的体验,不让各种垃圾消息的推送影响视觉,在ColorOS3.0上默认都屏蔽了第三方应用的通知栏权限,如果应用需要在通知栏展示Push通知的话还需要用户自己手动开启该应用的通知栏权限。同时,为防止奇形怪状的Push通知带来视觉不统一,ColorOS3.0会对不规范的Push通知进行拦截,导致第三方应用推送的Push显示不出来。下面先来对比下OPPO R7s和OPPO R9通知栏权限的区别。(前面是R7s,后面是R9)
可以看到,R7手机上面所有的app默认都可以显示通知栏,而R9却不是这样,除了系统应用跟白名单应用之外,其他的第三方应用全部默认不显示通知栏。那为什么QQ跟QQ空间默认可以显示呢?我了解到这是因为OPPO加了白名单,可以默认显示通知栏的白名单。OPPO这里给加的白名单app主要是一些强社交类的app,比如QQ,QQ空间,微信,微博等都在白名单之内,所以默认是可以显示通知栏的,那如果不在白名单内的app想要显示push通知怎么办呢?除了用户手动在系统设置里面打开开关之外还有其他方法吗?下面我们就来从OPPO源码的角度来分析一下这个开关能不能用我们的代码把它打开,如果可以的话其他的app也就可以实现默认打开通知栏权限了。
1:在OPPO ColorOS3.0上第三方应用能不能通过代码实现默认打开通知栏?
准备工具:1.OPPO R9手机 2.odex->dex工具 smali/baksmali 3.dex2jar 4.Java Decompiler
思路:为了查看系统应用对应位置的源代码,必须先找到该应用所对应的APK,要想找APK得先找到包名,这里既然我们要搜索的这个是通知管理类,且在android系统上是用Notification,NotificationManager,NotificationManagerService来显示和管理的,那它就应该是跟notification有关的包名。带着这个想法,我们先adb pull一把,把所有的系统应用拿出来,然后找相关的包名。
下面这些就是我们pull出来的系统应用,其实大部分都是见名知意的,像BackupAndRestore对应备份与恢复,Bluetooth对应蓝牙,Browser对应浏览器等等。
好,那我们继续往下找,就会找到一个notification相关的包名,其实就是我们的通知管理notification_center,打开文件夹可以看到一个apk跟一个odex文件,把apk解压缩却发现这里没有classes.dex文件,这明显是一个经过odex的apk,所以我们想要从apk中得到class文件从而得到源码的话,我们必须进行 odex——>dex——>jar——>class 的一系列转换。那我们一开始准备的工具就用上场了,具体步骤这里不多说了。(使用baksmali将odex——>dex,使用dex2jar将dex——>jar,使用Java Decompiler将jar——>class)
现在用Java Decompiler打开源码瞧一瞧。但是,到这一步又没有思路了,有了源码怎么看呢?从哪里下手呢?哪些是关键信息呢?这时候Logcat就大有用处了,打开手机来到设置里面的通知管理界面,打开或者关闭通知的开关,同时用关键词”notification”来筛选日志,可以找到两个相关的Activity类,我们可以从这两个Activity入手。
再次来到源码,在onPreferenceChange方法中可以看到关键的一段代码:如果不是黑名单应用(也就是如果是白名单应用),执行break。否则如果是黑名单应用则执行 Util.setNotificationsEnabled(this.mPkg, false, this.mContext);这个方法里面的false就很关键了,第三方应用能不能获取通知栏的权限就是通过setNotificationsEnabled方法来设置的,传入false,说明不给权限。
这里也就说明了,在ColorOS3.0上,非系统应用,非白名单应用是默认屏蔽掉通知栏权限的。那我们接下来看setNotificationsEnabled是怎么实现的,看能不能直接调用它来给第三方应用获取通知栏权限。
setNotificationsEnabled方法里面做了两个动作,一个是获取应用的Uid(系统应用的Uid跟第三方应用的Uid是有区别的),第二个是获取一个服务ServiceManager.getService(“notification”),其实这个服务就是NotificationManager,然后通过NotificationManager远程调用setNotificationsEnabledForPackage,其实这个setNotificationsEnabledForPackage是定义在NotificationManagerService里面IBinder的一个方法。那么下面我们就来看看NotificationManagerService中的setNotificationsEnabledForPackage方法是怎么实现的。
原来,setNotificationsEnabledForPackage方法首先通过判断当前调用者是否是系统调用来进行鉴权(校验Uid检查调用程序有没有权限),然后再执行setNotificationsEnabledForPackageImpl方法对通知栏权限进行设置。也就是说如果外部应用直接调用这里是会报错的(SecurityException)。所以也就证明了第三方应用是无法通过代码来默认打开通知栏权限的。那么这里可能又有人说可以通过修改我们应用的Uid为系统应用的Uid,然后伪装成系统应用从而达到目的。但是这种方法基本上不现实,因为修改Uid之后需要进行安装包重签名,而我们根本拿不到每个手机厂商系统应用的APK签名,且我们也不愿意这样做,因为我们会有自己的一个安全签名方式,也方便后期维护。所以,OPPO ColorOS3.0在这里出狠招屏蔽了第三方应用的通知栏权限,且第三方应用想要顺利显示push通知唯一的方法就是让用户主动到设置中心打开开关。
2.OPPO ColorOS3.0推荐的Push通知规范有什么要求?
了解到ColorOS3.0会屏蔽不规范的Push通知,对不规范的通知进行拦截,不予显示。下面是ColorOS3.0的标准通知要求:
a.在创建通知接口方面的规范:使用Android推荐的方式new Notification.Builder创建通知,不用new Notification来创建(这个方式已被google废弃掉),同时要指定通知的属性(icon,ticker等)。
b.在个性化方面的规范:为防止应用通知过快更新,ColorOS3.0限定通知刷新时间间隔必须大于100ms,否则不予显示,同时对自定义布局的通知进行屏蔽(为了统一视觉)。要想显示通知横幅,必须指定通知的ticker属性,且在系统设置通知中心内打开横幅的开关。
下面先看下第一点:推荐new Notification.Builder,废弃new Notification
接下来看第二点:屏蔽掉奇形怪状样式的通知。从OPPO的源码看他们的log很有意思。
一句话解释:黑名单应用的通知我们吞掉不让你显示
一句话解释:非标准样式的通知我们不让你显示,而且把你记下了
一句话解释:一系列奇形怪状的样式我们也不让你显示
一句话解释:不设定通知的小icon会让你的应用在android以后的某个版本上崩溃掉
最后再总结一下:
1.OPPO ColorOS3.0 上默认屏蔽第三方应用的通知栏权限,且暂时没有方法自己实现默认打开这个权限,但像QQ、QQ空间、微信、微博等强社交类app在白名单内不受影响。
2.OPPO ColorOS3.0 推荐使用Android标准方式创建通知,且限定通知刷新时间间隔必须大于100ms,同时会对奇形怪状的通知进行屏蔽不予显示。