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

 如果是簡單的一點應用,使用上面的方式獲取短信內容能夠滿足需求,但是如果對覆蓋率要求高一點的需求可能就不行了,特別是對彩信權限或者其他權限的依賴會很不方便,所以多數時候使用監聽系統短信數據庫內容變化的方式來獲取短信內容。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM