原文:http://www.bangchui.org/simple/?t32709.html
最近在网上搜索怎样绕开QQ通讯录和360的广播中断,在它们之前拿到短信,结果很遗憾没有搜索到什么东西,反而搜索到一些炫耀的帖子,只说自己解决了,不给别个分享解决方案的。
我只好自己来弄一下这个东东。
本来以为腾讯是拦截ril层的消息,然后阻断广播的发送,但是这种方式要修改framework才可能实现。
反编译QQ通讯录代码,没有看到特殊的设置,反编译360,发现了其中的奥秘。
这要从广播特别是有序广播的分发机制说起,底层的代码还有待我们的研究。
我没怎么看底层代码,只有测试实践。
经过我多次测试发现:
广播分2种,无序广播和有序广播。可以理解为散列和队列广播。
首先无序广播,不能中断,分发机制有点类似散列发送。这种广播的的发送为:context.sendBroadcast
这种广播是不能中断的,请看API说明。
其次为有序广播,可以中断。
这种广播,以我的理解可能存在很大的BUG,短信这块广播的发送BUG现在被QQ和360利用,可能在以后的版本中会修订。
它是一个按一种优先级顺序发送,即按某种messagequere队列发送,其中队列中的任何广播接收者都可以终端该广播,导致它之后的成员不能接收到广播。
我们首先看这个队列是怎么形成的:(以下是假想+测试验证,有待代码验证)
假设我们广播优先级别都设成:2147483647(注:最大int形整数)
首先动态注册优先级别最高
其次静态注册
在动态注册中
最早动态注册优先级别最高
在静态注册中
最早安装的程序,静态注册优先级别最高(注:安装APK会解析af.xml,把其加入队列)
注意这里安装 是指adb install xxx.apk或者手机上安装应用。
然后才是adb push到其他目录的应用
可能的原因是手机查询应用的时候会先去特定目录解析应用,所以广播注册会出现这种差别。adb push 到system/app下会比安装的优先级高吗?这有待你的验证,我还没验证。
然后都是安装的应用中
首先安装的优先等级最高
OK 以上就是我分析的有序广播的优先级别问题。
反编译360源码会发现,他首先静态注册的广播接收器里面设置的优先数量级为2147483647,然后在广播中启动一个service,在service中注册了一个优先数量级为2147483647的同样的广播接收器。
就这么简单就实现了没有同种实现的应用的情况下拦截短信。
OK,我们现在以同样的方式来做一个实验:
首先写一个应用,注册一个开机完成广播接收者。
<receiver android:name=".MyBrocast" android:permission="android.permission.BROADCAST_SMS">
<intent-filter android:priority="2147483647">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
<intent-filter android:priority="2147483647">
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
然后在这个广播接收者接收到信息之后马上启动一个service
public void onReceive(Context context, Intent intent) {
Log.v("MyBrocast.onReceive", "testtttttttttttt");
if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){
Intent service=new Intent(context, MyService.class);
context.startService(service);
}
然后在service重新注册一个接收短信的广播接收者,优先级2147483647
IntentFilter localIntentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
localIntentFilter.setPriority(2147483647);
myService = new MyBrocast();
MyBrocast localMessageReceiver = myService;
Log.v("MyBrocast.onReceive", "onCreate");
Intent localIntent = registerReceiver(localMessageReceiver, localIntentFilter);
这种重启手机就OK了,你也可以让360,QQ郁闷的。
当然这里分享这个信息主要是想和大家一起提高,360和QQ的android版还是做的相当好的,有许多我们借鉴的地方。
其实我们也可以认为这是telephone模块的一个BUG,据说以前的版本是无序广播,不知是否出于拦截短信的机制修改了,反正我觉得有被第3方应用滥用的可能。
测试源码很简单,这里就不贴了
这里测试代码有很多不完善,比如服务被杀或者没有启动什么的。我们可以考虑把服务进程挂载到重要进程上去。
<-----------------------------------------------更新分割线----------------------------------------------------->
前面理解可能有点偏差,下面是我研究了一下代码部分验证了我的推论,能力有限还没能完全验证。
这个方法说明,如果我们的优先级设置为最大int形整数是能够排在最前面的。
到这里我们发现,优先级设置最高的会排在前面,如果优先级相同则按默认的排序不动,那么现在默认的排序是怎么样的呢?
我们发现大部分广播发送者都是只传送一个action,所以我们关注:
if (resolvedType == null && scheme == null && intent.getAction() != null) {
firstTypeCut = mActionToFilter.get(intent.getAction());
if (debug) Slog.v(TAG, "Action list: " + firstTypeCut);
}
mActionToFilter内部list的排序决定了同样优先级情况下的排序问题
首先我们跟踪比较容易跟踪的
1.动态注册
AMS中的registerReceiver方法中有对变量mReceiverResolver的操作
如下:
BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
}
mReceiverResolver.addFilter(bf);
这里验证了同样优先级的情况下先动态注册的排在后来动态注册的广播接收者的前面。------------------1OK
2.静态注册
我们首先跟踪到PackageManagerService中的queryIntentReceivers方法
ComponentName comp = intent.getComponent();一般为空,略过。
因为广播一般不会发送给某个指定的包,所以最终一般会调用
if (pkgName == null) {
return (List<ResolveInfo>)mReceivers.queryIntent(intent,
resolvedType, flags);
}
这里会调用到IntentResolver的public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) 方法
这里同样是找mActionToFilter中的值,说明这个对象里面可以动态赋值给它(动态注册AMS中的registerReceiver方法中有对变量mReceiverResolver)
也可以静态赋值,那么我们现在关注的就是mf.xml中的赋值顺序。但是到这里跟踪不下去了,感觉是安装的时候packageManageservice做了某种特殊的设置,adb install和adb push也做了不同的操作,
我还没有研究PMS,希望高手能解释下
-------------2 not ok
继续回来看AMS的broadcastIntentLocked方法
if (!ordered && NR > 0) {
如果不是队列广播这里会先立即给动态注册的广播接收者发送消息 ------------------------------------3OK一部分
并且这里还说明了一个问题:就是非ordered的广播基本都是动态注册优先接收到信息,无论动态注册的优先级多低(有的情况也不是这样的,具体见源代码)
继续
if (receivers != null) {
这里只要有静态注册的一般不为空
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
while (it < NT && ir < NR) {
if (curt == null) {
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
curr = registeredReceivers.get(ir);
}
if (curr.getPriority() >= curt.priority) {
// Insert this broadcast record into the final list.
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
} else {
// Skip to the next ResolveInfo in the final list.
it++;
curt = null;
}
}
这一段仅仅是把动态注册的优先级一样的广播加在了静态注册的前面,这里完全验证了动态注册优先静态注册----------------------3OK
代码后面的就是发送广播了。