Android4.4,关于短信部分的结构做了比较大的变化,之前的版本可以直接编写一个apk通过拦截有序广播的方法来拦截短信,但到了4.4后改成另外一个广播SMS_DELIVER_ACTION,而且只有默认短信应用能接受到该广播,原来的广播也能接受到但不能实现拦截。如果普通的应用想拦截短信就必须设置为默认短信应用。
但如果你想拦截一些短信,而且不想限制用户选择自己喜欢的短信应用。我们只能在framework层的广播发送前拦截住。如果你还想实现动态的黑白名单,那就涉及进程间的通信。拦截某段代码还要涉及到如何保存恢复数据。
进程间的通信有2种,aidl和broadcast。选用改动少的broadcast。如果用aidl的还要加server,非常麻烦。
数据我们要随bradcast发送出去,所以要序列化。这样子白名单就可以正常。
目前网络上关于新结构的分析很少,所有我们要自己分析找到地方,直接在源码里搜索SMS_DELIVER_ACTION找到frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java,广播就是从这段代码开始发出。
boolean processMessagePart(InboundSmsTracker tracker) {
//(省略前面一段代码)
Intent intent;
if (destPort == -1) {
intent = new Intent(Intents.SMS_DELIVER_ACTION);
// Direct the intent to only the default SMS app. If we can't find a default SMS app
// then sent it to all broadcast receivers.
ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true);
if (componentName != null) {
// Deliver SMS message only to this receiver
intent.setComponent(componentName);
log("Delivering SMS to: " + componentName.getPackageName() +
" " + componentName.getClassName());
}
} else {
Uri uri = Uri.parse("sms://localhost:" + destPort);
intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri);
}
intent.putExtra("pdus", pdus);
intent.putExtra("format", tracker.getFormat());
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, resultReceiver);
}
分析调用这个方法的代码,以及从方法名和传入参数可以知道,InboundSmsTracker这个类是专门用于封装短信的所有相关信息包含短信内容,发送人号码。而且processMessagePart方法除了发送短信广播,还将原来过长的短信在发送时被分开,在这里将合并一起。
接下来就很简单了,注释以上代码,将InboundSmsTracker序列化(因为进程间通信的数据必须序列化),使用广播发送出去。
Intent smsIntent = new Intent("com.sms.SMS_RECEIVED_ACTION");
SmsMessage mSmsMessage = SmsMessage.createFromPdu(tracker.getPdu());
String sender=mSmsMessage.getOriginatingAddress();
smsIntent.putExtra("sender", sender);
smsIntent.putExtra("InboundSmsTracker", tracker);
smsIntent.putExtra("pdus", pdus);
if("CdmaInboundSmsHandler".equals(InboundSmsHandler.this.getName())){
smsIntent.putExtra("action", "com.flyaudio.sms.SMS_CDMA_INTERCEPTER");
}else if("GsmInboundSmsHandler".equals(InboundSmsHandler.this.getName())){
smsIntent.putExtra("action", "com.flyaudio.sms.SMS_GSM_INTERCEPTER");
}
mContext.sendBroadcast(smsIntent);
为了使白名单能正常接收,另外创建一个广播继续执行发送广播的代码
mBroadcast = new SmsIntercepterBroadcastReceiver();
IntentFilter filter = new IntentFilter();
if("CdmaInboundSmsHandler".equals(name)){
filter.addAction("com.sms.SMS_CDMA_INTERCEPTER");
}else if("GsmInboundSmsHandler".equals(name)){
filter.addAction("com.sms.SMS_GSM_INTERCEPTER");
}
mContext.registerReceiver(mBroadcast, filter);
在构造器里注册广播就可以了,这里有个地方要稍微注意一下。
这个InboundSmsHandler.java本身是个抽象类,从它的子类GsmInboundSmsHandler,CdmaInboundSmsHandler
以及打logcat测试时发现,不同的网络GSM和CDMA会使用不同的类处理。将其区分开,在黑白名单的应用里面判断一下网络类型再发送到适当的处理类。
广播里就是执行发送广播的代码,这里不再重复给出了。