前段時間(已經過去兩個月瞭….)公司讓搞一下android彩信的攔截與發送,於是就在網上找瞭一些資料,開始研究它的實現過程。
PS:需要從系統源碼中扣取部分文件,大概在30個左右,不知道能不能精簡,沒認真看過。這裡我重點說一下彩信的攔截和解析,因為彩信解析方面的資料相對較少。發送的部分我會提供一下我的參考文章,並且可能會轉載一下這篇文章,我就是通過這篇文章實現的彩信發送。
因為代碼量比較大,所以就隻貼下關鍵源碼,並且說下流程和要註意的問題。仔細搜索一下的話網上可以找到相關的demo和資料(主要是彩信發送方面的,解析的好像沒有),但是在使用時要註意,他們說的並不是全對的,某些方面給你誤導瞭,他們的整體流程和源碼都是好的,但是在一些點上刻意寫錯瞭(主要是pdu組包、圖片或附件的類型等)。
簡要說一下我的流程吧:
一、攔截彩信
1、註冊彩信接收器
彩信的攔截和網上百度或google 出來的一樣,都是註冊一個廣播接收器,然後把該接收器的權限設置成最大值,這個最大值不是網上說的1000而是2147483647(好像是整型的最大值)
在AdroidMainfest.xml裡的代碼如下:
[html] <!– MMS SMS接收器 –>
<receiver
android:name=".app.MmsSmsReceiver">
<intent-filter android:priority="2147483647">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
<intent-filter android:priority="2147483647">
<action
android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
<data
android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
<!– MMS SMS接收器 –>
<receiver
android:name=".app.MmsSmsReceiver">
<intent-filter android:priority="2147483647">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
<intent-filter android:priority="2147483647">
<action
android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
<data
android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
2、定義自己的廣播接收處理類
和普通的廣播接收一樣,我們要自己寫一個廣播接收處理的類,但是要在onReceive方法裡添加一句:abortBroadcast();這樣在我們攔截到該條彩信信息後,當執行這一句時,該系統廣播(就是接收到彩信的系統廣播)就不在繼續往下發送。
我的代碼:
PS:部分方法可能不通用,自己按自己的情況來。
[java] import com.shanzha.activity.InvalidHeaderValueException;
import com.shanzha.activity.MmsContent;
import com.shanzha.activity.PduHeaders;
import com.shanzha.activity.PduParser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MmsSmsReceiver extends BroadcastReceiver {
/**
* 接收短信
*/
public static final String SMS_RECEIVE_ACTION = "android.provider.Telephony.SMS_RECEIVED";
/**
* 接收彩信
*/
public static final String MMS_RECEIVE_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";
public static long date = 0;
Context context;
byte[] TransactionId;
@Override
public void onReceive(final Context context, Intent intent) {
// TODO Auto-generated method stub
this.context=context;
String action = intent.getAction();
//彩信
if(action.equals(MMS_RECEIVE_ACTION)){
PduParser parser = new PduParser();
try {
PduHeaders headers = parser.parseHeaders(intent.getByteArrayExtra("data"));
TransactionId = headers.getTransactionId();
if (headers.getMessageType() == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) {
//號碼獲取
String from = headers.getFrom();
final String content_location = headers.getContentLocation();
if (content_location != null) {
new Thread() {
public void run() {
MmsConnect mmsConnect = new MmsContent(context,content_location,TransactionId);
try {
mmsConnect.connect();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();}
}
}.start();
}
}
} catch (InvalidHeaderValueException e) {
// TODO Auto-generated catch block
e.printStackTrace();}
//廣播不在發送
abortBroadcast();
}
}
}
import com.shanzha.activity.InvalidHeaderValueException;
import com.shanzha.activity.MmsContent;
import com.shanzha.activity.PduHeaders;
import com.shanzha.activity.PduParser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MmsSmsReceiver extends BroadcastReceiver {
/**
* 接收短信
*/
public static final String SMS_RECEIVE_ACTION = "android.provider.Telephony.SMS_RECEIVED";
/**
* 接收彩信
*/
public static final String MMS_RECEIVE_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";
public static long date = 0;
Context context;
byte[] TransactionId;
@Override
public void onReceive(final Context context, Intent intent) {
// TODO Auto-generated method stub
this.context=context;
String action = intent.getAction();
//彩信
if(action.equals(MMS_RECEIVE_ACTION)){
PduParser parser = new PduParser();
try {
PduHeaders headers = parser.parseHeaders(intent.getByteArrayExtra("data"));
TransactionId = headers.getTransactionId();
if (headers.getMessageType() == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) {
//號碼獲取
String from = headers.getFrom();
final String content_location = headers.getContentLocation();
if (content_location != null) {
new Thread() {
public void run() {
MmsConnect mmsConnect = new MmsContent(context,content_location,TransactionId);
try {
mmsConnect.connect();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();}
}
}.start();
}
}
} catch (InvalidHeaderValueException e) {
// TODO Auto-generated catch block
e.printStackTrace();}
//廣播不在發送
abortBroadcast();
}
}
}
3、彩信內容的獲取與解析 (重點和難點,pdu的解析)
其實在第二步我們能獲得的僅僅是一個號碼(發送者的號碼)和一個url(彩信內容下載地址)地址(需要扣取系統的源碼來解析彩信信息)以及每條彩信的標識id,我們可以根據號碼選擇是不是把該條彩信屏蔽(當然還可以進行其他操作)。如果需要獲取彩信內容,就需要從我們獲得url地址下載彩信信息主體,下載獲得的數據是byte[]類型的,需要轉換才能成為可用數據(這一塊是重點,詳情參考系統源碼)。
我的關鍵代碼:
彩信數據(內容)下載:
[java] protected byte[] getPdu(String url) throws IOException {
// ensureRouteToHost(url, mTransactionSettings);
return HttpUtils.httpConnection(
context, -1L,
url, null, HttpUtils.HTTP_GET_METHOD,
true,
"10.0.0.172",
80);
}
protected byte[] getPdu(String url) throws IOException {
// ensureRouteToHost(url, mTransactionSettings);
return HttpUtils.httpConnection(
context, -1L,
url, null, HttpUtils.HTTP_GET_METHOD,
true,
"10.0.0.172",
80);
}
彩信數據解析:
[java] //下載彩信數據
mmsData=getPdu(contentLocation);
//彩信數據解析
PduBody body = null;
GenericPdu pdu = new PduParser(mmsData).parse();
if ((pdu == null) || (pdu.getMessageType() != 0x84)) {
Log.e("xml", "數據為空或類型錯誤");
}else if (pdu instanceof MultimediaMessagePdu) {
body = ((MultimediaMessagePdu) pdu).getBody();
//獲取主題 www.aiwalls.com
String subject=((MultimediaMessagePdu) pdu).getSubject().getString();
if (body != null) {
int partsNum = body.getPartsNum();
for (int i = 0; i < partsNum; i++) {
PduPart part = body.getPart(i);
//附件類型獲取
String contentType=new String( part.getContentType(),"gb2312");
//文本類型
if(contentType.contains("text")){
//文本內容獲取
String content= new EncodedStringValue(part.getData()).getString();
//記錄
MMs_Content.put("text",part.getData()); }
//jpg圖片類型
else if(contentType.contains("jpeg")){
//圖片文件生成
Bitmap bmp=BitmapFactory.decodeByteArray(part.getData(), 0,part.getData().length);
if(bmp!=null){
//記錄
MMs_Content.put("jpeg",part.getData());
}else
Log.i("xml","Bitmap is null");
}else{
//其他類型數據:音頻等,暫不處理。
}
}
}
}
//下載彩信數據
mmsData=getPdu(contentLocation);
//彩信數據解析
PduBody body = null;
GenericPdu pdu = new PduParser(mmsData).parse();
if ((pdu == null) || (pdu.getMessageType() != 0x84)) {
Log.e("xml", "數據為空或類型錯誤");
}else if (pdu instanceof MultimediaMessagePdu) {
body = ((MultimediaMessagePdu) pdu).getBody();
//獲取主題
String subject=((MultimediaMessagePdu) pdu).getSubject().getString();
if (body != null) {
int partsNum = body.getPartsNum();
for (int i = 0; i < partsNum; i++) {
PduPart part = body.getPart(i);
//附件類型獲取
String contentType=new String( part.getContentType(),"gb2312");
//文本類型
if(contentType.contains("text")){
//文本內容獲取
String content= new EncodedStringValue(part.getData()).getString();
//記錄
MMs_Content.put("text",part.getData()); }
//jpg圖片類型
else if(contentType.contains("jpeg")){
//圖片文件生成
Bitmap bmp=BitmapFactory.decodeByteArray(part.getData(), 0,part.getData().length);
if(bmp!=null){
//記錄
MMs_Content.put("jpeg",part.getData());
}else
Log.i("xml","Bitmap is null");
}else{
//其他類型數據:音頻等,暫不處理。
}
}
}
}
4、向彩信中心返回成功狀態信息。
當我們成功下載數據後要向向彩信中心返回成功的狀態(第三步解析獲得的彩信id),彩信中心才認為我們成功接收到彩信。
[java] //給彩信中心返回成功接收信息
NotifyRespInd notifyRespInd = new NotifyRespInd(
PduHeaders.CURRENT_MMS_VERSION,
TransactionId, PduHeaders.STATUS_RETRIEVED);
mHttpBox = new HttpBox(MMSC, new PduComposer(context,notifyRespInd).make());
mHttpBox.setConnectTimeout(50 * 1000);
mHttpBox.setReadTimeout(30 * 1000);
mHttpBox.setRequestMethod(true);
mHttpBox.addHeader("User-Agent","Nokia6120c/4.21 (SymbianOS/9.2; U; Series60/3.1 Nokia6120c/4.21; Profile/MIDP-2.0 Configuration/CLDC-1.1 ) Mozilla/5.0 AppleWebK");
mHttpBox.addHeader("Accept","*/*, application/vnd.wap.mms-message, application/vnd.wap.sic");
mHttpBox.addHeader("Content-Type","application/vnd.wap.mms-message");
mHttpBox.addHeader("Accept-Charset","iso-8859-1, utf-8; q=0.7, *; q=0.7");
mHttpBox.addHeader("Accept-Language","zh-cn, zh;q=1.0,en;q=0.5");
mHttpBox.connect();
// mHttpBox.read();
// mmsData = mHttpBox.getInData();
//給彩信中心返回成功接收信息
NotifyRespInd notifyRespInd = new NotifyRespInd(
PduHeaders.CURRENT_MMS_VERSION,
TransactionId, PduHeaders.STATUS_RETRIEVED);
mHttpBox = new HttpBox(MMSC, new PduComposer(context,notifyRespInd).make());
mHttpBox.setConnectTimeout(50 * 1000);
mHttpBox.setReadTimeout(30 * 1000);
mHttpBox.setRequestMethod(true);
mHttpBox.addHeader("User-Agent","Nokia6120c/4.21 (SymbianOS/9.2; U; Series60/3.1 Nokia6120c/4.21; Profile/MIDP-2.0 Configuration/CLDC-1.1 ) Mozilla/5.0 AppleWebK");
mHttpBox.addHeader("Accept","*/*, application/vnd.wap.mms-message, application/vnd.wap.sic");
mHttpBox.addHeader("Content-Type","application/vnd.wap.mms-message");
mHttpBox.addHeader("Accept-Charset","iso-8859-1, utf-8; q=0.7, *; q=0.7");
mHttpBox.addHeader("Accept-Language","zh-cn, zh;q=1.0,en;q=0.5");
mHttpBox.connect();
// mHttpBox.read();
// mmsData = mHttpBox.getInData();
二、發送彩信
這部分相對而言沒有那麼復雜,難點是pdu的組包,很多問題都是由組包不正確引起的,另外需要註意的一點是:要註意APN的切換,這樣才能提高發送成功的成功率。這一部分的資料相對很多啊,我就不貼代碼瞭。註意我開篇說的問題就行瞭,自己多試一下吧。
這裡提供一篇作為參考的文章,我就是根據這篇文章實現的彩信發送,但是同樣要註意我開篇說的問題哦。
摘自 agods–足跡