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 条评论
登录 后参与评论

相关文章

来自专栏移动开发面面观

ProductFlavors 简单使用

我们在开发过程中,会经常遇到,同样的业务逻辑,需要配置不同的资源的情况。有时是不同的渠道,有时是不同的语言环境,各种不同。

562
来自专栏代码散人

Vapor3初探——使用Fluent查询数据

Vapor升级到Vapor3的时间恰巧赶上WWDC,本来想从美国回来就开始写一些关于Vapor3的文章,但是WWDC信息量太大了,直到现在才转过来写一点关于Va...

853
来自专栏包子铺里聊IT

DAG、Workflow 系统设计、Airflow 与开源的那些事儿

DAG (Directed Acyclic Graph) 是一个非常有用、也有很有意思的数据结构。如果说数组、链表、二叉树这类数据结构是学习中的基础,那么 DA...

3444
来自专栏算法+

音频压缩编码 opus 附完整C++代码示例

绝大数人都知道mp3格式编码,以及aac,amr等压缩格式编码。 而在语音通信界有一个强悍的音频格式编码opus. 经过实测,压缩比最高可以达到1:10。 10...

5447
来自专栏上善若水

015android初级篇之传感器的简单使用

要监控传感器的原始数据,你需要实现 SensorEventListener 接口的 onAccuracyChanged() 和onSensorChanged()...

1155
来自专栏后端之路

爬虫的故事

1、爬虫占总PV比例较高,这样浪费钱(尤其是三月份爬虫)。 三月份爬虫是个什么概念呢?每年的三月份我们会迎接一次爬虫高峰期。 最初我们百思不得其解。直到有一次,...

2537
来自专栏Android 开发者

玩转全新的 Android 8.0 Oreo 后台策略

2474
来自专栏GreenLeaves

抽象工厂(AbstractFactory)模式-创建型模式

1、new 的问题 常见的对象创建方法: //创建一个Road对象 Road road=new Road(); new的问题:实现依赖,不能应对具体实例的变化 ...

1916
来自专栏JavaEE

java生成二维码前言:java生成二维码demo:总结:

先聊聊题外话,话说在1994年,日本的丰田汽车公司独立出来了一个电装公司。由于高精度的汽车零配件需要匹配很多信息,而传统的条形码容量有限,在电装公司的腾弘原的带...

822
来自专栏Android开发实战

设计模式-适配器模式

适配器模式:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

965

扫码关注云+社区