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

相关文章

来自专栏张善友的专栏

Miguel de Icaza 细说 Mix 07大会上的Silverlight和DLR

Mono之父Miguel de Icaza 详细报道微软Mix 07大会上的Silverlight和DLR ,上面还谈到了Mono and Silverligh...

3037
来自专栏芋道源码1024

熔断器 Hystrix 源码解析 —— 断路器 HystrixCircuitBreaker

本文主要基于 Hystrix 1.5.X 版本 1. 概述 2. HystrixCircuitBreaker 3. HystrixCircuitBreaker....

5857
来自专栏张善友的专栏

LINQ via C# 系列文章

LINQ via C# Recently I am giving a series of talk on LINQ. the name “LINQ via C...

3055
来自专栏Ceph对象存储方案

Luminous版本PG 分布调优

Luminous版本开始新增的balancer模块在PG分布优化方面效果非常明显,操作也非常简便,强烈推荐各位在集群上线之前进行这一操作,能够极大的提升整个集群...

3735
来自专栏一个会写诗的程序员的博客

Spring Reactor 项目核心库Reactor Core

Non-Blocking Reactive Streams Foundation for the JVM both implementing a Reactiv...

2902
来自专栏张善友的专栏

Silverlight + Model-View-ViewModel (MVVM)

     早在2005年,John Gossman写了一篇关于Model-View-ViewModel模式的博文,这种模式被他所在的微软的项目组用来创建Expr...

3358
来自专栏张善友的专栏

Mix 10 上的asp.net mvc 2的相关Session

Beyond File | New Company: From Cheesy Sample to Social Platform Scott Hansel...

2797
来自专栏陈仁松博客

ASP.NET Core 'Microsoft.Win32.Registry' 错误修复

今天在发布Asp.net Core应用到Azure的时候出现错误InvalidOperationException: Cannot find compilati...

5318
来自专栏闻道于事

js登录滑动验证,不滑动无法登陆

js的判断这里是根据滑块的位置进行判断,应该是用一个flag判断 <%@ page language="java" contentType="text/html...

8938
来自专栏一个爱瞎折腾的程序猿

sqlserver使用存储过程跟踪SQL

USE [master] GO /****** Object: StoredProcedure [dbo].[sp_perfworkload_trace_s...

3070

扫码关注云+社区