frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/gsmSMSDispatcher.java
gsmSMSDispatcher.java 里有很多文件都调用了 sendRawPdu()
sendTextWithEncodingType()
sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent, destAddr);
frameworks/opt/telephony/src/java/com/android/internal/telephony/SMSDispatcher.java
下定义了 4 个类
public abstract class SMSDispatcherextends Handler
以下3个类都是 嵌套在SMSDispatcher 类当中的。
private static class SettingsObserverextends ContentObserver
protected static final class SmsTracker
private final class ConfirmDialogListener
发送sms是通过 如下sendTextWithEncodingType函数来的。它是被 gemini 层的相关函数调用,然后发送的。
关于 它是被哪儿调用的,我们可以参考 “http://blog.csdn.net/duanlove/article/details/9499539”。
那我们看看sendTextWithEncodingType的实现。
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/gsmSMSDispatcher.java
// MTK-END [ALPS00094284] Filter hyphen of phone number by mtk80589 in 2011.11.23
// MTK-END [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
// MTK-START [ALPS00094531] Orange feature SMS Encoding Type Setting by mtk80589 in 2011.11.22
/** {@inheritDoc} */
protected void sendTextWithEncodingType( //单条发送,不需要拆分,就调用这个。
String destAddr,
String scAddr,
String text,
int encodingType,
PendingIntent sentIntent,
PendingIntent deliveryIntent) {
// impl
// MTK_OPTR_PROTECT_START
if (isDmLock == true) {
Log.d(TAG, "DM status: lock-on");
return;
}
// MTK_OPTR_PROTECT_END
int encoding = encodingType;
TextEncodingDetails details = SmsMessage.calculateLength(text, false); // 需理解的重点1.
if (encoding != details.codeUnitSize &&
(encoding == SmsConstants.ENCODING_UNKNOWN ||
encoding == SmsConstants.ENCODING_7BIT)) {
Log.d(TAG, "[enc conflict between details[" + details.codeUnitSize
+ "] and encoding " + encoding);
details.codeUnitSize = encoding;
}
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( // 需理解的重点2.
scAddr, destAddr, text, (deliveryIntent != null),
null, encoding, details.languageTable, details.languageShiftTable);
if (pdu != null) {
sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent, destAddr); // 需理解的重点3
} else {
Log.d(TAG, "sendText: pdu is null");
if (sentIntent != null) {
try {
sentIntent.send(RESULT_ERROR_NULL_PDU);
} catch (CanceledException ex) {
Log.e(TAG, "failed to send back RESULT_ERROR_NULL_PDU");
}
}
}
}
/** {@inheritDoc} */
protected void sendMultipartTextWithEncodingType( // 需要拆分成多条发送,就使用这个函数
String destAddr,
String scAddr,
ArrayList
int encodingType,
ArrayList
ArrayList
// impl
// MTK_OPTR_PROTECT_START
if (isDmLock == true) {
Log.d(TAG, "DM status: lock-on");
return;
}
// MTK_OPTR_PROTECT_END
int refNumber = getNextConcatenatedRef() & 0xff;
int msgCount = parts.size();
int encoding = encodingType;
mRemainingMessages = msgCount;
TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
for (int i = 0; i < msgCount; ++i) {
TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
if (encoding != details.codeUnitSize &&
(encoding == SmsConstants.ENCODING_UNKNOWN ||
encoding == SmsConstants.ENCODING_7BIT)) {
Log.d(TAG, "[enc conflict between details[" + details.codeUnitSize
+ "] and encoding " + encoding);
details.codeUnitSize = encoding;
}
encodingForParts[i] = details;
}
for (int i = 0; i < msgCount; ++i) {// 按照拆分的条数,一条一条发送。
SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
concatRef.refNumber = refNumber;
concatRef.seqNumber = i + 1; // 1-based sequence
concatRef.msgCount = msgCount;
// TODO: We currently set this to true since our messaging app will never
// send more than 255 parts (it converts the message to MMS well before that).
// However, we should support 3rd party messaging apps that might need 16-bit
// references
// Note: It's not sufficient to just flip this bit to true; it will have
// ripple effects (several calculations assume 8-bit ref).
concatRef.isEightBits = true;
SmsHeader smsHeader = new SmsHeader();
smsHeader.concatRef = concatRef;
if (encoding == SmsConstants.ENCODING_7BIT) {
smsHeader.languageTable = encodingForParts[i].languageTable;
smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
}
PendingIntent sentIntent = null;
if (sentIntents != null && sentIntents.size() > i) {
sentIntent = sentIntents.get(i);
}
PendingIntent deliveryIntent = null;
if (deliveryIntents != null && deliveryIntents.size() > i) {
deliveryIntent = deliveryIntents.get(i);
}
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, parts.get(i), (deliveryIntent != null),
SmsHeader.toByteArray(smsHeader), encoding,
smsHeader.languageTable, smsHeader.languageShiftTable);
if (pdu != null) {
sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent, destAddr);
} else {
Log.d(TAG, "sendText: pdu is null");
if (sentIntent != null) {
try {
sentIntent.send(RESULT_ERROR_NULL_PDU);
} catch (CanceledException ex) {
Log.e(TAG, "failed to send back RESULT_ERROR_NULL_PDU");
}
}
}
}
}
GsmAlphabet.java (fram\base\telephony\java\com\android\internal\telephony)
package com.android.internal.telephony;
import android.content.res.Resources;
import android.text.TextUtils;
import android.util.SparseIntArray;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import com.android.internal.telephony.SmsConstants;
import com.android.internal.R;
import java.util.ArrayList;
import java.util.List;
/**
* This class implements the character set mapping between
* the GSM SMS 7-bit alphabet specified in TS 23.038 6.2.1
* and UTF-16
*
* {@hide}
*/
public class GsmAlphabet {
private static final String TAG = "GSM";
private GsmAlphabet() { }
/**
* This escapes extended characters, and when present indicates that the
* following character should be looked up in the "extended" table.
*
* gsmToChar(GSM_EXTENDED_ESCAPE) returns 0xffff
*/
public static final byte GSM_EXTENDED_ESCAPE = 0x1B;
/**
* User data header requires one octet for length. Count as one septet, because
* all combinations of header elements below will have at least one free bit
* when padding to the nearest septet boundary.
*/
public static final int UDH_SEPTET_COST_LENGTH = 1;
/**
* Using a non-default language locking shift table OR single shift table
* requires a user data header of 3 octets, or 4 septets, plus UDH length.
*/
public static final int UDH_SEPTET_COST_ONE_SHIFT_TABLE = 4;
/**
* Using a non-default language locking shift table AND single shift table
* requires a user data header of 6 octets, or 7 septets, plus UDH length.
*/
public static final int UDH_SEPTET_COST_TWO_SHIFT_TABLES = 7;
/**
* Multi-part messages require a user data header of 5 octets, or 6 septets,
* plus UDH length.
*/
public static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6;
/**
* For a specific text string, this object describes protocol
* properties of encoding it for transmission as message user
* data.
*/
public static class TextEncodingDetails {
/**
*The number of SMS's required to encode the text.
*/
public int msgCount;
/**
* The number of code units consumed so far, where code units
* are basically characters in the encoding -- for example,
* septets for the standard ASCII and GSM encodings, and 16
* bits for Unicode.
*/
public int codeUnitCount;
/**
* How many code units are still available without spilling
* into an additional message.
*/
public int codeUnitsRemaining;
/**
* The encoding code unit size (specified using
* android.telephony.SmsMessage ENCODING_*).
*/
public int codeUnitSize;
/**
* The GSM national language table to use, or 0 for the default 7-bit alphabet.
*/
public int languageTable;
/**
* The GSM national language shift table to use, or 0 for the default 7-bit extension table.
*/
public int languageShiftTable;
// MTK-START [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
public boolean useSingleShift = false;
public boolean useLockingShift = false;
public int shiftLangId = -1;
// MTK-END [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
@Override
public String toString() {
return "TextEncodingDetails " +
"{ msgCount=" + msgCount +
", codeUnitCount=" + codeUnitCount +
", codeUnitsRemaining=" + codeUnitsRemaining +
", codeUnitSize=" + codeUnitSize +
", languageTable=" + languageTable +
", languageShiftTable=" + languageShiftTable +
" }";
}
}
对于 sms 流程中非常重要的一个 函数 sendRawPdu 定义于SMSDispatcher类当中。我们来看看它的实现:
/**
* Send a SMS
*
* @param smsc the SMSC to send the message through, or NULL for the
* default SMSC
* @param pdu the raw PDU to send
* @param sentIntent if not NULL this Intent
is
* broadcast when the message is successfully sent, or failed.
* The result code will be Activity.RESULT_OK
for success,
* or one of these errors:
* RESULT_ERROR_GENERIC_FAILURE
* RESULT_ERROR_RADIO_OFF
* RESULT_ERROR_NULL_PDU
* RESULT_ERROR_NO_SERVICE
.
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntent if not NULL this Intent
is
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
* @param destAddr the destination phone number (for short code confirmation)
*/
protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, PendingIntent deliveryIntent, String destAddr)
{
if (mSmsSendDisabled)
{
if (sentIntent != null)
{
try {
sentIntent.send(RESULT_ERROR_NO_SERVICE);
} catch (CanceledException ex) {}
}
Log.d(TAG, "Device does not support sending sms.");
return;
}
if (pdu == null)
{
if (sentIntent != null)
{
try {
sentIntent.send(RESULT_ERROR_NULL_PDU);
} catch (CanceledException ex) {}
}
return;
}
HashMap
map.put("smsc", smsc);
map.put("pdu", pdu);
// Get calling app package name via UID from Binder call
PackageManager pm = mContext.getPackageManager();
String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
if (packageNames == null || packageNames.length == 0)
{
// Refuse to send SMS if we can't get the calling package name.
Log.e(TAG, "Can't get calling app package name: refusing to send SMS");
if (sentIntent != null)
{
try
{
sentIntent.send(RESULT_ERROR_GENERIC_FAILURE);
} catch (CanceledException ex) {
Log.e(TAG, "failed to send error result");
}
}
return;
}
// MTK-START
/* Because it may have multiple apks use the same uid, ex. Mms.apk and omacp.apk, we need to
* exactly find the correct calling apk. We should use running process to check the correct
* apk. If we could not find the process via pid, this apk may be killed. We will use the
* default behavior, find the first package name via uid.
*/
if (packageNames.length > 1)
{
int callingPid = Binder.getCallingPid();
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
List processList = am.getRunningAppProcesses();
Iterator index = processList.iterator();
while (index.hasNext())
{
ActivityManager.RunningAppProcessInfo processInfo = (ActivityManager.RunningAppProcessInfo)(index.next());
if (callingPid == processInfo.pid)
{
packageNames[0] = processInfo.processName;
break;
}
}
}
// MTK-END
// Get package info via packagemanager
PackageInfo appInfo = null;
try
{
// XXX this is lossy- apps can share a UID
appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Can't get calling app package info: refusing to send SMS");
if (sentIntent != null) {
try {
sentIntent.send(RESULT_ERROR_GENERIC_FAILURE);
} catch (CanceledException ex) {
Log.e(TAG, "failed to send error result");
}
}
return;
}
// Strip non-digits from destination phone number before checking for short codes
// and before displaying the number to the user if confirmation is required.
SmsTracker tracker = new SmsTracker(map, sentIntent, deliveryIntent, appInfo,
PhoneNumberUtils.extractNetworkPortion(destAddr));
// checkDestination() returns true if the destination is not a premium short code or the
// sending app is approved to send to short codes. Otherwise, a message is sent to our
// handler with the SmsTracker to request user confirmation before sending.
// checkDestination(tracker): true ! mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS): false
// 此打印表明 并没有进入下面的第二个if语句,sendMessage 没有被调用。
if (checkDestination(tracker))
{
// check for excessive outgoing SMS usage by this app
if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) { // 下面并没有执行
sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
return;
}
// 流程到这里,继续往下走
int ss = mPhone.getServiceState().getState();
// ss != ServiceState.STATE_IN_SERVICE: false ;表明,并没有执行 handleNotInService(ss, tracker.mSentIntent);
if (ss != ServiceState.STATE_IN_SERVICE)
{
handleNotInService(ss, tracker.mSentIntent);
} else
{ // 实际上,走到了下面这个分支
String appName = getAppNameByIntent(sentIntent);
// MTK-START [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
// MTK_SMS_FILTER_SUPPORT true, call createMessageFromSubmitPdu
if(FeatureOption.MTK_SMS_FILTER_SUPPORT == true) { // 短信正常,进入这里
SmsMessage msg = createMessageFromSubmitPdu(smsc, pdu); // 从PDU创建 SmsMessage 类,以方便下面的 checkSmsWithNqFilter 函数使用
if(msg != null)
{ // 短信不为空,进入if分支; 上面的msg 是为了方便获取 sms的目的地址 和 消息体!以检查是否需要过滤!
boolean ret = checkSmsWithNqFilter(msg.getDestinationAddress(), msg.getMessageBody(), sentIntent);
if(ret == false)
{
Log.d(TAG, "[NQ this message is safe");
if (mUsageMonitor.check(appName, SINGLE_PART_SMS))
{
sendSms(tracker);// 发送短信 分支1
} else
{
sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));//发送短信 分支1
}
} else
{
Log.d(TAG, "[NQ this message may deduct fees");
SmsHeader.ConcatRef newConcatRef = null;
if(msg.getUserDataHeader() != null) {
newConcatRef = msg.getUserDataHeader().concatRef;
}
if(newConcatRef != null) {
if(sConcatRef == null || sConcatRef.refNumber != newConcatRef.refNumber) {
Log.d(TAG, "[NQ this is a new concatenated message, just update");
sConcatRef = newConcatRef;
//sConcatMsgCount = 1;
sendMessage(obtainMessage(EVENT_HANDLE_REDUCTED_MESSAGE, tracker));
} else {
Log.d(TAG, "[NQ this is the same concatenated message, keep previous operation");
//mSTrackers.add(tracker);
sConcatMsgCount += 1;
}
} else {
Log.d(TAG, "[NQ this is a non-concatenated message");
//sConcatMsgCount = 0;
sendMessage(obtainMessage(EVENT_HANDLE_REDUCTED_MESSAGE, tracker));
}
}
} else
{
Log.d(TAG, "[NQ fail to create message from pdu");
if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) {
sendSms(tracker);
} else {
sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
}
}
} else
{
// MTK-END [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) {
sendSms(tracker);
} else {
sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
}
// MTK-START [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
}
// MTK-END [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
}
// 短信发送完成,直接跳到这里。
}
}
同一个文件也定义了:class SmsTracker 和 sendSms
/**
* Send the message along to the radio.
*
* @param tracker holds the SMS message to send
*/
protected abstract void sendSms(SmsTracker tracker);
protected static final class SmsTracker
{
// fields need to be public for derived SmsDispatchers
public final HashMap
public int mRetryCount;
public int mMessageRef;
public final PendingIntent mSentIntent;
public final PendingIntent mDeliveryIntent;
public final PackageInfo mAppInfo;
public final String mDestAddress;
//构造函数
public SmsTracker(HashMap
PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr)
{
mData = data;
mSentIntent = sentIntent;
mDeliveryIntent = deliveryIntent;
mRetryCount = 0;
mAppInfo = appInfo;
mDestAddress = destAddr;
}
/**
* Returns whether this tracker holds a multi-part SMS.
* @return true if the tracker holds a multi-part SMS; false otherwise
*/
protected boolean isMultipart() {
HashMap map = mData;
return map.containsKey("parts");
}
}
而 以下文件中也定义了 sendSms 函数
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/SMSDispatcher.java
class GsmSMSDispatcher 中定义了:
/** {@inheritDoc} */
@Override
protected void sendSms(SmsTracker tracker)
{
HashMap
byte smsc[] = (byte[]) map.get("smsc");
byte pdu[] = (byte[]) map.get("pdu");
// MTK-START [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
synchronized (mSTrackersQueue)
{
if (mSTrackersQueue.isEmpty() || mSTrackersQueue.get(0) == tracker) {// 如果队列为空,且当前第一个消息是本消息,那么,就发送这个消息。
// MTK-END [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
mCm.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply);// 发送消息
// MTK-START [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
}
if (mSTrackersQueue.isEmpty() || mSTrackersQueue.get(0) != tracker) { // 如果队列为空,且第一个不是 本消息 ,那么就把本消息加入到队列中去。
Log.d(TAG, "Add tracker into the list: " + tracker); //如果 队列不为空,第一个 消息,也不是本消息,还是把本消息加入到队列中去。
mSTrackersQueue.add(tracker);
}
}
// MTK-END [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
}
GsmSMSDispatcher 实际上是 SMSDispatcher 的一个 子类,也就是派生类。
public final class GsmSMSDispatcherextendsSMSDispatcher
它还包含了一个 嵌套类:
private static final class SmsCbConcatInfo{
private final SmsCbHeader mHeader;
private final SmsCbLocation mLocation;
public SmsCbConcatInfo(SmsCbHeader header, SmsCbLocation location) {
mHeader = header;
mLocation = location;
}
@Override
public int hashCode() {
return (mHeader.getSerialNumber() * 31) + mLocation.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SmsCbConcatInfo) {
SmsCbConcatInfo other = (SmsCbConcatInfo)obj;
// Two pages match if they have the same serial number (which includes the
// geographical scope and update number), and both pages belong to the same
// location (PLMN, plus LAC and CID if these are part of the geographical scope).
return mHeader.getSerialNumber() == other.mHeader.getSerialNumber()
&& mLocation.equals(other.mLocation);
}
return false;
}
/**
* Compare the location code for this message to the current location code. The match is
* relative to the geographical scope of the message, which determines whether the LAC
* and Cell ID are saved in mLocation or set to -1 to match all values.
*
* @param plmn the current PLMN
* @param lac the current Location Area (GSM) or Service Area (UMTS)
* @param cid the current Cell ID
* @return true if this message is valid for the current location; false otherwise
*/
public boolean matchesLocation(String plmn, int lac, int cid) {
return mLocation.isInLocationArea(plmn, lac, cid);
}
}
#### 下面我们看一下 createMessageFromSubmitPdu返回 android.telephony.SmsMessage 的这个流程 ######
GsmSMSDispatcher 中的createMessageFromSubmitPdu 可以从 一个PDU BYTE数组创建一个 android.telephony.SmsMessage 类。
protected android.telephony.SmsMessage createMessageFromSubmitPdu(byte[] smsc, byte[] tpdu)
{
// smsc + tpdu
Log.d(TAG, "[NQ tpdu first byte is " + tpdu[0]);
int tpduLen = tpdu.length;
int smscLen = 1;
if (smsc != null) {// 如果 smsc不为null的话 ,就取出 smsc的长度 赋值给 smscLen ,否则 smscLen 就是 默认为1 。
smscLen = smsc.length;
} else {
Log.d(TAG, "[NQ smsc is null");
}
byte[] msgPdu = new byte[smscLen + tpduLen];
int curIndex = 0;
try {
if (smsc != null) { //smsc :需要注意的是smsc第一个byte的低八位 代表了发送者地址的长度。
System.arraycopy(smsc, 0, msgPdu, curIndex, smscLen);// // 如果 smsc不为null的话 , 就先把smsc (发送者的地址)存入到 msgPdu
} else {
msgPdu[0] = 0; // 否则的话,就只占一个byte,并且这个 byte是第一个byte,内容是 0
}
curIndex += smscLen;
System.arraycopy(tpdu, 0, msgPdu, curIndex, tpduLen); // 接着把剩下的tpdu部分原始的pdu数据,追加到 smsc后面。 其实到这里为止做的主要工作就是把发送者的地址和pdu数据整合到一起。并准备传入下面的函数。
Log.d(TAG, "[NQ mti byte in msgPdu is " + msgPdu[1]);
} catch (IndexOutOfBoundsException e) {
Log.d(TAG, "[NQ out of bounds error when copy pdu data");
}
return android.telephony.SmsMessage.createFromPdu(msgPdu, getFormat());
}
@Override
protected String getFormat() {
return SmsConstants.FORMAT_3GPP; // import com.android.internal.telephony.SmsConstants;
}
frameworks/opt/telephony/src/java/android/telephony/SmsMessage.java:
package android.telephony;
import android.os.Parcel;
import android.util.Log;
import com.android.internal.telephony.EncodeException;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.SmsConstants;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
import com.mediatek.common.featureoption.FeatureOption;
import java.lang.Math;
import java.util.ArrayList;
import java.util.Arrays;
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
/**
* A Short Message Service message.
*/
public class SmsMessage 类中:
public SmsMessageBase mWrappedSmsMessage;
public static class SubmitPdu{
public byte[] encodedScAddress; // Null if not applicable.
public byte[] encodedMessage;
public String toString() {
return "SubmitPdu: encodedScAddress = "
+ Arrays.toString(encodedScAddress)
+ ", encodedMessage = "
+ Arrays.toString(encodedMessage);
}
/**
* @hide
*/
protected SubmitPdu(SubmitPduBase spb) {
this.encodedMessage = spb.encodedMessage;
this.encodedScAddress = spb.encodedScAddress;
}
}
private SmsMessage(SmsMessageBase smb) {
mWrappedSmsMessage = smb;
}
对 createFromPdu 进行了重载:
*/
public static SmsMessage createFromPdu(byte[] pdu)
{
int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
String format = (PHONE_TYPE_CDMA == activePhone) ?
SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
return createFromPdu(pdu, format);
}
/**
* Create an SmsMessage from a raw PDU with the specified message format. The
* message format is passed in the {@code SMS_RECEIVED_ACTION} as the{@code format}
* String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
* or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
*
* @param pdu the message PDU from the SMS_RECEIVED_ACTION intent
* @param format the format extra from the SMS_RECEIVED_ACTION intent
* @hide pending API council approval
*/
public static SmsMessage createFromPdu(byte[] pdu, String format)
{
SmsMessageBase wrappedMessage;
if (SmsConstants.FORMAT_3GPP2.equals(format)) {
wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
} else if (SmsConstants.FORMAT_3GPP.equals(format)) { // 因为传入的是FORMAT_3GPP 所以走这个分支
wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu); //java/com/android/internal/telephony/gsm/SmsMessage.java 中 的 createFromPdu 根据 pdu参数生成的对象,返回到 wrappedMessage
} else {
Log.e(LOG_TAG, "createFromPdu(): unsupported message format " + format);
return null;
}
return new SmsMessage(wrappedMessage); //返回了一个SmsMessage 对象 . 那最后 SmsMessage 构造函数通过 传入上面包含信息的的 wrappedMessage 对象 构造了一个 android.telephony.SmsMessage对象。
}
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/SmsMessage.java:
package com.android.internal.telephony.gsm;
import android.os.Parcel;
import android.telephony.PhoneNumberUtils;
import android.text.format.Time;
import android.util.Log;
import com.android.internal.telephony.EncodeException;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import static com.android.internal.telephony.SmsConstants.MessageClass;
import static com.android.internal.telephony.SmsConstants.ENCODING_UNKNOWN;
import static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
// MTK-START [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
import android.os.SystemProperties;
import android.util.Config;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import static android.telephony.SmsMessage.MWI_VIDEO;
// MTK-END [ALPS000xxxxx] MTK code port to ICS added by mtk80589 in 2011.11.16
import com.android.internal.telephony.IMessageWaitingExt;
/**
* A Short Message Service message.
*
*/
public class SmsMessageextendsSmsMessageBase
在这个SmsMessage 中也定义了 createFromPdu 这个函数
/**
* Create an SmsMessage from a raw PDU.
*/
public static SmsMessage createFromPdu(byte[] pdu) {
try {
SmsMessage msg = new SmsMessage();
msg.parsePdu(pdu); // 解析PDU。其实就是根据原始的PDU 解析出来的各种东东存在msg对象里头。 于是下行返回的msg就具备有用的信息了。
return msg; //实际上最后返回的是这个SmsMessage 类型的实例
} catch (RuntimeException ex) {
Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
return null;
}
}
同时我们也可以看到上面调用的函数parsePdu 来解析 PDU byte数组:
/**
* TS 27.005 3.1, <pdu> definition "In the case of SMS: 3GPP TS 24.011 [6]
* SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
* ME/TA converts each octet of TP data unit into two IRA character long
* hex number (e.g. octet with integer value 42 is presented to TE as two
* characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
* something else...
*/
private void parsePdu(byte[] pdu) {
mPdu = pdu; // 首先把原始的pdu保存在 全局变量mPdu里头。这样我们可以随时取出原始的PDU
// Log.d(LOG_TAG, "raw sms message:");
// Log.d(LOG_TAG, s);
PduParser p = new PduParser(pdu); // 通过 PduParser 的构造函数解析PDU
scAddress = p.getSCAddress(); //获取解析好的原始地址。
if (scAddress != null) {
if (false) Log.d(LOG_TAG, "SMS SC address: " + scAddress);
}
// TODO(mkf) support reply path, user data header indicator
// TP-Message-Type-Indicator
// 9.2.3
int firstByte = p.getByte();
mti = firstByte & 0x3; //根据第一byte的 值进行 各自的解析。
switch (mti) {
// TP-Message-Type-Indicator
// 9.2.3
case 0:
case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved.
//This should be processed in the same way as MTI == 0 (Deliver)
parseSmsDeliver(p, firstByte);
break;
case 1:
parseSmsSubmit(p, firstByte);
break;
case 2:
parseSmsStatusReport(p, firstByte);
break;
default:
// TODO(mkf) the rest of these
throw new RuntimeException("Unsupported message type");
}
}
/**
* Parses a SMS-STATUS-REPORT message.
*
* @param p A PduParser, cued past the first byte.
* @param firstByte The first byte of the PDU, which contains MTI, etc.
*/
private void parseSmsStatusReport(PduParser p, int firstByte) {
isStatusReportMessage = true;
// TP-Status-Report-Qualifier bit == 0 for SUBMIT
forSubmit = (firstByte & 0x20) == 0x00;
// TP-Message-Reference
messageRef = p.getByte();
// TP-Recipient-Address
recipientAddress = p.getAddress();
// TP-Service-Centre-Time-Stamp
scTimeMillis = p.getSCTimestampMillis();
// TP-Discharge-Time
dischargeTimeMillis = p.getSCTimestampMillis();
// TP-Status
status = p.getByte();
messageBody = "";
// The following are optional fields that may or may not be present.
if (p.moreDataPresent()) {
// TP-Parameter-Indicator
int extraParams = p.getByte();
int moreExtraParams = extraParams;
while (((moreExtraParams & 0x80) != 0) && (p.moreDataPresent() == true)) {
// We only know how to parse a few extra parameters, all
// indicated in the first TP-PI octet, so skip over any
// additional TP-PI octets.
moreExtraParams = p.getByte();
}
// As per 3GPP 23.040 section 9.2.3.27 TP-Parameter-Indicator,
// only process the byte if the reserved bits (bits3 to 6) are zero.
if ((extraParams & 0x78) == 0) {
// TP-Protocol-Identifier
if ((extraParams & 0x01) != 0) {
protocolIdentifier = p.getByte();
}
// TP-Data-Coding-Scheme
if ((extraParams & 0x02) != 0) {
dataCodingScheme = p.getByte();
}
// TP-User-Data-Length (implies existence of TP-User-Data)
if ((extraParams & 0x04) != 0) {
boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
parseUserData(p, hasUserDataHeader);
}
}
}
}
private void parseSmsDeliver(PduParser p, int firstByte) {
replyPathPresent = (firstByte & 0x80) == 0x80;
originatingAddress = p.getAddress();
if (originatingAddress != null) {
if (false) Log.v(LOG_TAG, "SMS originating address: "
+ originatingAddress.address);
}
// TP-Protocol-Identifier (TP-PID)
// TS 23.040 9.2.3.9
protocolIdentifier = p.getByte();
// TP-Data-Coding-Scheme
// see TS 23.038
dataCodingScheme = p.getByte();
if (false) {
Log.v(LOG_TAG, "SMS TP-PID:" + protocolIdentifier
+ " data coding scheme: " + dataCodingScheme);
}
scTimeMillis = p.getSCTimestampMillis();
if (false) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
parseUserData(p, hasUserDataHeader);
}
/**
* Parses the User Data of an SMS.
*
* @param p The current PduParser.
* @param hasUserDataHeader Indicates whether a header is present in the
* User Data.
*/
private void parseUserData(PduParser p, boolean hasUserDataHeader) {
boolean hasMessageClass = false;
boolean userDataCompressed = false;
int encodingType = ENCODING_UNKNOWN;
// Look up the data encoding scheme
if ((dataCodingScheme & 0x80) == 0) {
// Bits 7..4 == 0xxx
automaticDeletion = (0 != (dataCodingScheme & 0x40));
userDataCompressed = (0 != (dataCodingScheme & 0x20));
hasMessageClass = (0 != (dataCodingScheme & 0x10));
if (userDataCompressed) {
Log.w(LOG_TAG, "4 - Unsupported SMS data coding scheme "
+ "(compression) " + (dataCodingScheme & 0xff));
} else {
switch ((dataCodingScheme >> 2) & 0x3) {
case 0: // GSM 7 bit default alphabet
encodingType = ENCODING_7BIT;
break;
case 2: // UCS 2 (16bit)
encodingType = ENCODING_16BIT;
break;
case 1: // 8 bit data
case 3: // reserved
Log.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
+ (dataCodingScheme & 0xff));
encodingType = ENCODING_8BIT;
break;
}
}
} else if ((dataCodingScheme & 0xf0) == 0xf0) {
automaticDeletion = false;
hasMessageClass = true;
userDataCompressed = false;
if (0 == (dataCodingScheme & 0x04)) {
// GSM 7 bit default alphabet
encodingType = ENCODING_7BIT;
} else {
// 8 bit data
encodingType = ENCODING_8BIT;
}
} else if ((dataCodingScheme & 0xF0) == 0xC0
|| (dataCodingScheme & 0xF0) == 0xD0
|| (dataCodingScheme & 0xF0) == 0xE0) {
// 3GPP TS 23.038 V7.0.0 (2006-03) section 4
// 0xC0 == 7 bit, don't store
// 0xD0 == 7 bit, store
// 0xE0 == UCS-2, store
if ((dataCodingScheme & 0xF0) == 0xE0) {
encodingType = ENCODING_16BIT;
} else {
encodingType = ENCODING_7BIT;
}
userDataCompressed = false;
boolean active = ((dataCodingScheme & 0x08) == 0x08);
// bit 0x04 reserved
if ((dataCodingScheme & 0x03) == 0x00) {
isMwi = true;
mwiSense = active;
mwiDontStore = ((dataCodingScheme & 0xF0) == 0xC0);
} else {
isMwi = false;
Log.w(LOG_TAG, "MWI for fax, email, or other "
+ (dataCodingScheme & 0xff));
}
} else if ((dataCodingScheme & 0xC0) == 0x80) {
// 3GPP TS 23.038 V7.0.0 (2006-03) section 4
// 0x80..0xBF == Reserved coding groups
if (dataCodingScheme == 0x84) {
// This value used for KSC5601 by carriers in Korea.
encodingType = ENCODING_KSC5601;
} else {
Log.w(LOG_TAG, "5 - Unsupported SMS data coding scheme "
+ (dataCodingScheme & 0xff));
}
} else {
Log.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
+ (dataCodingScheme & 0xff));
}
// set both the user data and the user data header.
int count = p.constructUserData(hasUserDataHeader,
encodingType == ENCODING_7BIT);
this.userData = p.getUserData();
this.userDataHeader = p.getUserDataHeader();
switch (encodingType) {
case ENCODING_UNKNOWN:
case ENCODING_8BIT:
messageBody = null;
break;
case ENCODING_7BIT:
messageBody = p.getUserDataGSM7Bit(count,
hasUserDataHeader ? userDataHeader.languageTable : 0,
hasUserDataHeader ? userDataHeader.languageShiftTable : 0);
break;
case ENCODING_16BIT:
messageBody = p.getUserDataUCS2(count);
break;
case ENCODING_KSC5601:
messageBody = p.getUserDataKSC5601(count);
break;
}
if (false) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'");
if (messageBody != null) {
parseMessageBody();
}
if (!hasMessageClass) {
messageClass = MessageClass.UNKNOWN;
} else {
switch (dataCodingScheme & 0x3) {
case 0:
messageClass = MessageClass.CLASS_0;
break;
case 1:
messageClass = MessageClass.CLASS_1;
break;
case 2:
messageClass = MessageClass.CLASS_2;
break;
case 3:
messageClass = MessageClass.CLASS_3;
break;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public MessageClass getMessageClass() {
return messageClass;
}
############# 从PDU返回 msg 结束##################
mCm 是定义于 “frameworks/opt/telephony/src/java/com/android/internal/telephony/SMSDispatcher.java” 中的
public abstract class SMSDispatcher extends Handler 类中。即 SMSDispatcher 类中定义了:
protected final CommandsInterface mCm;
frameworks/opt/telephony/src/java/com/android/internal/telephony/CommandsInterface.java:
/**
* smscPDU is smsc address in PDU form GSM BCD format prefixed
* by a length byte (as expected by TS 27.005) or NULL for default SMSC
* pdu is SMS in PDU format as an ASCII hex string
* less the SMSC address
*/
void sendSMS (String smscPDU, String pdu, Message response);
frameworks/opt/telephony/src/java/com/android/internal/telephony/UsimDataDownloadCommands.java:
UsimDataDownloadCommands类中:
@Override
public void sendSMS(String smscPDU, String pdu, Message response) {
}
以上两个文件中,值看到 sendSMS的定义,但却没看到包含有具体的实现。如果有人知道为什么,请告诉我。谢谢!
frameworks/opt/telephony/src/java/com/android/internal/telephony/SMSDispatcher.java:
/**
* This list is used to maintain the unsent Sms Tracker
* we have this queue list to avoid we send a lot of SEND_SMS request to RIL
* and block other commands.
* So we only send the next SEND_SMS request after the previously request has been completed
*/
protected ArrayList
在 frameworks\opt\telephony\src\java\com\android\internal\telephony\ril.java
public final class RIL extendsBaseCommands implements CommandsInterface
最后追溯到,ril 类中有 sendSMS的实现。如下:
public void sendSMS (String smscPDU, String pdu, Message result)
{
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);
rr.mp.writeInt(2);
rr.mp.writeString(smscPDU);
rr.mp.writeString(pdu);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
send(rr);
}
private void send(RILRequest rr) {
Message msg;
boolean show = (requestToString(rr.mRequest).compareTo("LAST_CALL_FAIL_CAUSE") == 0);
if (show) riljLog("###send start");
if (mSocket == null) {
if (show) riljLog("###mSocket == null");
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
return;
}
//正常的发送流程应该走这里:
if (show) riljLog("###send 1");
msg = mSender.obtainMessage(EVENT_SEND, rr);
if (show) riljLog("###send 2");
acquireWakeLock();
if (show) riljLog("###send 3");
msg.sendToTarget(); // 正式发送消息
if (show) riljLog("###send end");
}
msg.sendToTarget() 定义在如下文件中的Message 类中。
frameworks\base\core\java\android\os\Message.java :
Handler target; // target 是 handler类型的
/**
* Sends this Message to the Handler specified by {@link #getTarget}.
* Throws a null pointer exception if this field has not been set.
*/
public void sendToTarget() {
target.sendMessage(this);
}
那么 再看 target.sendMessage(this); 的 sendMessage 方法 定义于:
frameworks\base\core\java\android\os\Handler.java :
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) uptimeMillis.
* The time-base is {@link android.os.SystemClock#uptimeMillis}.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
/// MSG Logger Manager @}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
frameworks\base\core\java\android\os\MessageQueue.java :
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
/// M: Protection of sending the same message instance
if(IS_ENG_BUILD)
{
Message tempP = mMessages;
while(tempP != null)
{
if(tempP == msg)
{
Log.wtf("MessageQueue", "Warning: Sending the same message instance! Ignored. msg=" + msg);
return false;
}//if
tempP = tempP.next;
}//while
}//if
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
public class MessageQueue 类中 有定义如下变量:
/// M: for enqueueMessage debug enhancement used
private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
// True if the message queue can be quit.
private final boolean mQuitAllowed;
@SuppressWarnings("unused")
private int mPtr; // used by native code
http://wenku.baidu.com/view/33a31767783e0912a2162a88.html