Android 短信拦截及用途分析
监听系统短信这个只能作为一个技术点来研究下,读者可能在工作中可能不会哦涉及到,一般的应用软件也不会有这个需求
但是作为程序员呢,多了解一下也是好的。
Android 监听系统短信有什么用?
1、对系统接收到的短信进行识别,是广告或者是诈骗等
2、对短信内容进行过滤或者是对内容进行提取,比如验证码提取
3、对系统短信进行拦截,连系统自己都不让收到了(不会出现在系统数据里面,也不会有系统短信的通知栏提示)
监听系统短信广播有什么坑?
1、系统短信广播为有序广播,要拦截的话,需要在注册广播的时候设置广播优先级为最大,不过这种也有风险,如果被其他的应用先拦截了,那么我们将不再收到,使用时需注意。
2、要接到系统短信广播,那么应用必须具备短信读取权限,这对使用者来说可能是一个限制
3、除了短信读取权限,有些手机需要同时具备彩信读取权限(小米手机),这个就有点苛刻了
4、如果不能够接受第3点,那么要使用另外一种方式获取短信内容了,那就是:通过监听系统短信数据库数据变化,这个单独写了一篇文章介绍http://www.cnblogs.com/popfisher/p/5455980.html
5、系统短信数据库也是通过监听短信广播的方式得到短信内容数据的,只是系统自己的东西它有默认权限允许,不担心因为权限问题收不到短信广播
第5点可以这样验证:自己写一个短信广播的接收者,把短信广播给拦截了,会发现系统自己也收不到短信内容了。
如果是上面几种场景你都可是通过监听系统短信广播,然后解析出系统短信的内容, 进而对短信内容进行其他相关处理
监听系统短信广播代码如下
private static class SmsReceiver extends BroadcastReceiver { SmsReceiverProcessor mSmsReceiverProcessor; SmsReceiver() { mSmsReceiverProcessor = new SmsReceiverProcessor(); } @Override public void onReceive(Context context, Intent intent) { if (intent == null) { return; } String action = intent.getAction(); if (SmsReceiverProcessor.ANDROID_PROVIDER_TELEPHONY_SMS_RECEIVED.equals(action) || SmsReceiverProcessor.ANDROID_PROVIDER_TELEPHONY_SMS_RECEIVED2.equals(action) || SmsReceiverProcessor.ANDROID_PROVIDER_TELEPHONY_SMS_RECEIVED_2.equals(action) || SmsReceiverProcessor.ANDROID_PROVIDER_TELEPHONY_GSM_SMS_RECEIVED.equals(action)){ mSmsReceiverProcessor.handleSms(intent); } // 如果需要拦截广播,调用下面语句 abortBroadcast(); } } public class SmsReceiverProcessor { public static final String ANDROID_PROVIDER_TELEPHONY_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"; public static final String ANDROID_PROVIDER_TELEPHONY_SMS_RECEIVED2 = "android.provider.Telephony.SMS_RECEIVED2"; public static final String ANDROID_PROVIDER_TELEPHONY_SMS_RECEIVED_2 = "android.provider.Telephony.SMS_RECEIVED_2"; public static final String ANDROID_PROVIDER_TELEPHONY_GSM_SMS_RECEIVED = "android.provider.Telephony.GSM_SMS_RECEIVED"; public SmsReceiverProcessor() { } public void handleSms(Intent intent) { SmsMessage[] smss = SmsUtils.getMessagesFromIntent(intent); if (smss != null && smss.length >= 1) { StringBuilder bodyBuf = new StringBuilder(); String phoneNumber = ""; // 电话号码 long time = 0; for (SmsMessage msg : smss) { try { bodyBuf.append(msg.getDisplayMessageBody()); phoneNumber = msg.getDisplayOriginatingAddress(); time = msg.getTimestampMillis(); } catch (Exception e) { e.printStackTrace(); continue; } } final String smsContent = bodyBuf.toString(); // 短信内容 if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(smsContent)) { return; } // 获得短信号码和内容之后可以进行相关处理 System.out.println("phoneNumber: " + phoneNumber + " smsContent: " + smsContent); } } } // SmsUtils.java代码 public class SmsUtils { public static SmsMessage[] getMessagesFromIntent(Intent intent) { // moto的双模手机 if (isMotoTwoMode()) { return getMessagesFromIntentInMotoXT800(intent); } Object[] messages = (Object[]) intent.getSerializableExtra("pdus"); if (messages == null) { return null; } final int pduCount = messages.length; SmsMessage[] msgs = new SmsMessage[pduCount]; try { for (int i = 0; i < pduCount; i++) { msgs[i] = SmsMessage.createFromPdu((byte[]) messages[i]); } } catch (Throwable e) { return null; } return msgs; } private static SmsMessage[] getMessagesFromIntentInMotoXT800(Intent intent) { String strFrom = intent.getStringExtra("from"); boolean bCDMA; if (strFrom == null) return null; if (strFrom.equals("GSM")) { bCDMA = false; } else if (strFrom.equals("CDMA")) { bCDMA = true; } else { return null; } Object[] messages = (Object[]) intent.getSerializableExtra("pdus"); byte[][] pduObjs = new byte[messages.length][]; for (int i = 0; i < messages.length; i++) { pduObjs[i] = (byte[]) messages[i]; } byte[][] pdus = new byte[pduObjs.length][]; int pduCount = pdus.length; SmsMessage[] msgs = new SmsMessage[pduCount]; for (int i = 0; i < pduCount; i++) { pdus[i] = pduObjs[i]; SmsMessageBase obj = XT800CreateFromPdu(pdus[i], bCDMA); try { msgs[i] = SmsMessage.class.newInstance(); Field f = SmsMessage.class.getField("mWrappedSmsMessage"); f.set(msgs[i], obj); } catch (Exception e) { e.printStackTrace(); } } return msgs; } // MOTO xt800上面的短信解析 private static SmsMessageBase XT800CreateFromPdu(byte[] pdu, boolean bCDMA) { SmsMessageBase wrappedMessage = null; if (bCDMA) { wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu); } else { wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu); } return wrappedMessage; } // 判断是否是摩托的双模手机 public static boolean isMotoTwoMode() { final String strXT800 = "XT800"; final String strXT800plus = "XT800+"; final String strXT806 = "XT806"; final String strXT882 = "XT882"; String model = Build.MODEL; if (model != null) { String upper = model.toUpperCase(); if (upper.equals(strXT800) || upper.equals(strXT800plus) || upper.equals(strXT806) || upper.equals(strXT882)) { return true; } } return false; } }
上面的代码需要导入两个类如下: import android.telephony.SmsMessage; import com.android.internal.telephony.SmsMessageBase;
接下来是注册广播,这里使用动态注册的方式,广播的注册与反注册结合Activity或者Service的生命周期来使用,具体不再详述。
广播的使用
private static BroadcastReceiver mSmsReceiver = null; private static void registerSmsReceiver(ContextWrapper contextWrapper) { try { mSmsReceiver = new SmsReceiver(); IntentFilter filter = new IntentFilter(SmsReceiverProcessor.ANDROID_PROVIDER_TELEPHONY_SMS_RECEIVED); filter.addAction(SmsReceiverProcessor.ANDROID_PROVIDER_TELEPHONY_SMS_RECEIVED2); filter.addAction(SmsReceiverProcessor.ANDROID_PROVIDER_TELEPHONY_SMS_RECEIVED_2); filter.addAction(SmsReceiverProcessor.ANDROID_PROVIDER_TELEPHONY_GSM_SMS_RECEIVED); filter.setPriority(Integer.MAX_VALUE); // 这里虽然这是为整数最大值,但是实际上应该不允许超过系统运行的最大值1000,没验证 contextWrapper.registerReceiver(mSmsReceiver, filter, Manifest.permission.BROADCAST_SMS, null); } catch(Throwable e) { } } private static void unregisterSmsReceiver(ContextWrapper contextWrapper) { try { contextWrapper.unregisterReceiver(mSmsReceiver); } catch(Exception e) { } }
如果是简单的一点应用,使用上面的方式获取短信内容能够满足需求,但是如果对覆盖率要求高一点的需求可能就不行了,特别是对彩信权限或者其他权限的依赖会很不方便,所以多数时候使用监听系统短信数据库内容变化的方式来获取短信内容。