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) {
        
    }
}

如果是简单的一点应用,使用上面的方式获取短信内容能够满足需求,但是如果对覆盖率要求高一点的需求可能就不行了,特别是对彩信权限或者其他权限的依赖会很不方便,所以多数时候使用监听系统短信数据库内容变化的方式来获取短信内容。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏debugeeker的专栏

sedna进行xquery查询

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/detai...

861
来自专栏菩提树下的杨过

ExtJs+WCF+LINQ实现分页Grid

上篇文章《用ExtJs+Linq+Wcf打造简单grid 》,这个网格控件不带分页,本文在上文的基础上添加分页功能,文中会着重介绍如何在用LINQ返回分页数据,...

3597
来自专栏二进制文集

爬取北京摩拜单车信息(附分析过程和详细代码)

去年11月份可以在微信中抓取摩拜的小程序,但是现在不行了。当时微信小程序的API很简陋,利用代理可以直接抓取。但是现在试下挂上代理小程序都打不开了。

5966
来自专栏游戏杂谈

Android判断用户的网络类型(2/3/4G、wifi)

很多时候需要先判断当前用户的网络,才会继续之后的一些处理逻辑。但网络类型获取这一块,我用我自己的的手机调试时遇到一些问题,这里记录一下。

1382
来自专栏杂烩

分布式服务框架之Dubbo整合Spring项目(二)

1092
来自专栏数据结构与算法

BZOJ3262: 陌上花开(cdq分治)

第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值。

1392
来自专栏HansBug's Lab

1455: 罗马游戏

1455: 罗马游戏 Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 721  Solved: 272 [Subm...

31310
来自专栏GIS讲堂

Arcgis for JS实现台风运动路径与影像范围的显示

3992
来自专栏ACM小冰成长之路

51Nod-1149-Pi的递推式

ACM模版 描述 ? 题解 image.png 代码 #include <cstdio> #include <algorithm> #include <cmat...

2237
来自专栏用户2442861的专栏

Android游戏开发十日通(6)- 太空大战

http://blog.csdn.net/silangquan/article/details/16921035

1392

扫码关注云+社区

领取腾讯云代金券