Android短彩信源码解析-短信发送流程(二)

时间:2022-12-20 15:40:04

转载请注明出处:http://blog.csdn.net/droyon/article/details/11699935

2,短彩信发送framework逻辑

短信在SmsSingleRecipientSender.java中包装了SentIntents,以及DeliveryIntents,信息的内容在message中,信息的目的发送地址在mDest中,然后调用下面的代码进行信息的发送

smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents);

smsMessager对应的类为:SmsManager.java

2.1  进入SmsManager.java

public void sendMultipartTextMessage(
String destinationAddress, String scAddress, ArrayList<String> parts,
ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
if (parts == null || parts.size() < 1) {
throw new IllegalArgumentException("Invalid message body");
} if (parts.size() > 1) {
try {
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
if (iccISms != null) {
iccISms.sendMultipartText(destinationAddress, scAddress, parts,
sentIntents, deliveryIntents);
}
} catch (RemoteException ex) {
// ignore it
}
} else {
PendingIntent sentIntent = null;
PendingIntent deliveryIntent = null;
if (sentIntents != null && sentIntents.size() > 0) {
sentIntent = sentIntents.get(0);
}
if (deliveryIntents != null && deliveryIntents.size() > 0) {
deliveryIntent = deliveryIntents.get(0);
}
sendTextMessage(destinationAddress, scAddress, parts.get(0),
sentIntent, deliveryIntent);
}
}

在这个类中,主要是根据parts的数量,进行跨进程调用ISms服务。如果是多条信息,执行:

iccISms.sendMultipartText(destinationAddress, scAddress, parts,
sentIntents, deliveryIntents);

这里调用的ISms服务的sendMultipartText方法。

如果是单条信息,执行sendTextMessage

public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
} if (TextUtils.isEmpty(text)) {
throw new IllegalArgumentException("Invalid message body");
} try {
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
if (iccISms != null) {
iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
}
} catch (RemoteException ex) {
// ignore it
}
}

同样的,要调用ISms服务。只不过方法变成了Isms服务的sendText方法

关于iSms服务的注册,它是在初始化phone进程时注册的

public IccSmsInterfaceManagerProxy(IccSmsInterfaceManager
iccSmsInterfaceManager) {
this.mIccSmsInterfaceManager = iccSmsInterfaceManager;
if(ServiceManager.getService("isms") == null) {
ServiceManager.addService("isms", this);
}
}

然后我们进到IccSmsInterfaceManagerProxy.java中

2.2  IccSmsManagerProxy.java

    public void sendText(String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
mIccSmsInterfaceManager.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
} public void sendMultipartText(String destAddr, String scAddr,
List<String> parts, List<PendingIntent> sentIntents,
List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
mIccSmsInterfaceManager.sendMultipartText(destAddr, scAddr,
parts, sentIntents, deliveryIntents);
}

代理类中进行实现

2.3 iccSmsManager.java

    public void sendMultipartText(String destAddr, String scAddr, List<String> parts,
List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
mPhone.getContext().enforceCallingPermission(
"android.permission.SEND_SMS",
"Sending SMS message");
if (Log.isLoggable("SMS", Log.VERBOSE)) {
int i = 0;
for (String part : parts) {
log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
", part[" + (i++) + "]=" + part);
}
}
mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
(ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents);
}
    public void sendText(String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
mPhone.getContext().enforceCallingOrSelfPermission(
"android.permission.SEND_SMS",
"Sending SMS message");
if (Log.isLoggable("SMS", Log.VERBOSE)) {
log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
" text='"+ text + "' sentIntent=" +
sentIntent + " deliveryIntent=" + deliveryIntent);
}
mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
}

最终他们都要调到mDispatcher的sendText或者sendMultipartText方法,mDispatcher的原型类:SMSDispatcher,它有两个子类,GsmSmsDispatcher.java以及CdmaSmsDispatcher.java

2.4,GsmSmsDispatcher.java

protected void sendMultipartText(String destAddr, String scAddr,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveryIntents) { int refNumber = getNextConcatenatedRef() & 0x00FF;
int msgCount = parts.size();
int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN; mRemainingMessages = msgCount; TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
for (int i = 0; i < msgCount; i++) {
TextEncodingDetails details = calculateLength(parts.get(i), false);
if (encoding != details.codeUnitSize
&& (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN
|| encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
encoding = details.codeUnitSize;
}
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; // Set the national language tables for 3GPP 7-bit encoding, if enabled.
if (encoding == android.telephony.SmsMessage.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);
} sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding,
sentIntent, deliveryIntent, (i == (msgCount - 1)));
} }

遍历所有的短信part,然后执行sendNewSubmitPdu方法,这个方法在SmsDispatcher.java中是abstract的,真正的实现在其子类中:

protected void sendNewSubmitPdu(String destinationAddress, String scAddress,
String message, SmsHeader smsHeader, int encoding,
PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
if (pdu != null) {
sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
} else {
Log.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
}
}

实现方式为执行sendRawPdu。

以上是sendMultipartsText,让我们看看sendText方法在SmsDispatcher.java中的实现:

protected abstract void sendText(String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent);

SmsDispatcher.java的设计方法为让其子类来决定sendText方法的实现方式,现贴出sendText的源码实现:

protected void sendText(String destAddr, String scAddr, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null));
if (pdu != null) {
sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
} else {
Log.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
}
}

我们看到其实现方式为执行sendRawPdu,到这里也就是说,无论是sendText还是sendMultipartsText方法,他们归根结底都是执行sendRawPdu方法,不同的是sendMultipartsText是遍历所有的短信parts,然后在调用sendRawPdu。

在这个方法的前一步会将信息的内容,要发送的目的地址,当前的系统时间等打包成SubmitPdu,关于短信pdu这部分,会稍后介绍,我们现在主要解析一下短信framework发送的主要逻辑。

下面我们看看sendRawPdu的方法实现:

protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
PendingIntent deliveryIntent) {
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<String, Object> map = new HashMap<String, Object>();
map.put("smsc", smsc);
map.put("pdu", pdu); SmsTracker tracker = new SmsTracker(map, sentIntent,
deliveryIntent);
int ss = mPhone.getServiceState().getState(); if (ss != ServiceState.STATE_IN_SERVICE) {
handleNotInService(ss, tracker);
} else {
String appName = getAppNameByIntent(sentIntent);
if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) {
sendSms(tracker);
} else {
sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
}
}
}

在这个方法里,首先会检查一些状态,例如pdu是否为null,smsSendDisabled等,然后包装一个SmsTracher对象,在发送前还要检查一下Phone进程的状态,是否处于“离线”状态,关于这个状态,moderm会根据当前所处的信号强度,做出改变。

如果当前处于服务中,那么就可以进行我们短信的发送了,发送调用sendMessage或者sendSms进行短信的发送,关于sendMessage这个方法,参数为Message,message生成代码:

public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}

也就是说这个message是和Handler相关的message。

PS:我不知道大家有没有注意,其实SmsDispatcher.java这个类就是个Handler。

在发送之前,会调用:

mUsageMonitor.check(appName, SINGLE_PART_SMS)

进行检查,所作的事情其实就是检查在一段时间内,等待发送的短信数目不超过MAX值,关于等待时间以及MAX数目:

SmsUsageMonitor.java

/** Default checking period for SMS sent without user permission. */
private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000; /** Default number of SMS sent in checking period without user permission. */
private static final int DEFAULT_SMS_MAX_COUNT = 100;

回到短信发送中,在此处,检查通过,故短信发送执行sendSms(tracker)

protected void sendSms(SmsTracker tracker) {
HashMap<String, Object> map = tracker.mData; byte smsc[] = (byte[]) map.get("smsc");
byte pdu[] = (byte[]) map.get("pdu"); Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
mCm.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply);
}

其中mCm对象是CommandInterface的引用

protected final CommandsInterface mCm;

很多人对它不是很熟悉,但一定很熟悉它的其中一个子类,那就是RIL.java。

也就是说,短信发送流程,在此处会调用RIL.java,执行sendSMS方法,参数为smsc的pdu字符串,短信内容以及时间等的pdu,以及一个Message,WHAT值为EVENT_SEND_SMS_COMPLETE,tracher对象作为其Object。我们进入到RIL.java中

2.5,RIL.java

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

关于RIL.java,它会和reference_ril.c进行配合,进行at指令的发送以及接收反馈等信息。reference_ril.c文件会被编译到reference-ril.so中。

在此处,RIL.java会将相应的pdu等信息打包,然后通过socket发送到reference_ril.c中,相关处理如下:

2.6,reference_ril.c

static void requestSendSMS(void *data, size_t datalen, RIL_Token t)
{
int err;
const char *smsc;
const char *pdu;
int tpLayerLength;
char *cmd1, *cmd2;
RIL_SMS_Response response;
ATResponse *p_response = NULL; smsc = ((const char **)data)[0];
pdu = ((const char **)data)[1]; tpLayerLength = strlen(pdu)/2; // "NULL for default SMSC"
if (smsc == NULL) {
smsc= "00";
} asprintf(&cmd1, "AT+CMGS=%d", tpLayerLength);
asprintf(&cmd2, "%s%s", smsc, pdu); err = at_send_command_sms(cmd1, cmd2, "+CMGS:", &p_response); if (err != 0 || p_response->success == 0) goto error; memset(&response, 0, sizeof(response)); /* FIXME fill in messageRef and ackPDU */ RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
at_response_free(p_response); return;
error:
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
at_response_free(p_response);
}

调用AT命令,cmgs将信息发往moderm,进行相关流程处理。

最后,如果短信成功发送出去,还记得我们在发送时,从GsmSmsDispatcher.java传递到RIL.java中的Message吗,在发送之后,RIL.java会回调此Message,这个Message如下:

Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);

前面说了,What = EVENT_SEND_SMS_COMPLETE,Object = tricker。我们之前也说了SmsDispacher.java以及其子类是Handler,那么这个Message在那里处理的那?答案是在SmsDispacher.java中,处理逻辑代码如下:

case EVENT_SEND_SMS_COMPLETE:
// An outbound SMS has been successfully transferred, or failed.
handleSendComplete((AsyncResult) msg.obj);
break;
protected void handleSendComplete(AsyncResult ar) {
SmsTracker tracker = (SmsTracker) ar.userObj;
PendingIntent sentIntent = tracker.mSentIntent; if (ar.exception == null) {
if (false) {
Log.d(TAG, "SMS send complete. Broadcasting "
+ "intent: " + sentIntent);
} if (tracker.mDeliveryIntent != null) {//wanghailu,hailushijie@163.com ,hlwang
// Expecting a status report. Add it to the list.
int messageRef = ((SmsResponse)ar.result).messageRef;
tracker.mMessageRef = messageRef;
deliveryPendingList.add(tracker);
} if (sentIntent != null) {
try {
if (mRemainingMessages > -1) {
mRemainingMessages--;
} if (mRemainingMessages == 0) {
Intent sendNext = new Intent();
sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true);
sentIntent.send(mContext, Activity.RESULT_OK, sendNext);
} else {
sentIntent.send(Activity.RESULT_OK);
}
} catch (CanceledException ex) {}
}
} else {
if (false) {
Log.d(TAG, "SMS send failed");
} int ss = mPhone.getServiceState().getState(); if (ss != ServiceState.STATE_IN_SERVICE) {
handleNotInService(ss, tracker);
} else if ((((CommandException)(ar.exception)).getCommandError()
== CommandException.Error.SMS_FAIL_RETRY) &&
tracker.mRetryCount < MAX_SEND_RETRIES) {
// Retry after a delay if needed.
// TODO: According to TS 23.040, 9.2.3.6, we should resend
// with the same TP-MR as the failed message, and
// TP-RD set to 1. However, we don't have a means of
// knowing the MR for the failed message (EF_SMSstatus
// may or may not have the MR corresponding to this
// message, depending on the failure). Also, in some
// implementations this retry is handled by the baseband.
tracker.mRetryCount++;
Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
} else if (tracker.mSentIntent != null) {
int error = RESULT_ERROR_GENERIC_FAILURE; if (((CommandException)(ar.exception)).getCommandError()
== CommandException.Error.FDN_CHECK_FAILURE) {
error = RESULT_ERROR_FDN_CHECK_FAILURE;
}
// Done retrying; return an error to the app.
try {
Intent fillIn = new Intent();
if (ar.result != null) {
fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);
}
if (mRemainingMessages > -1) {
mRemainingMessages--;
} if (mRemainingMessages == 0) {
fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
} tracker.mSentIntent.send(mContext, error, fillIn);
} catch (CanceledException ex) {}
}
}
}

至此,短信发送流程大致如此,短信接收流程中framework部分的处理大致是发送流程的反方向。

首先检查AsyncResult对象中是否存在异常,如果成功发送的信息,那么不存在异常,如果发送失败,那么是存在Exception的,会进行异常的相应的逻辑处理,大体流程相似,故本处介绍无异常时的流程逻辑。

无论发送成功还是失败,大体都是执行tricker对象中的mSentIntents,这是一个PendingIntent,执行send会发送此广播,那么我们的上层应用Mms中的SmsReceiverService.java会收到这个广播,并进行相应的逻辑处理,逻辑代码大体如下:

private void handleSmsSent(Intent intent, int error) {
Uri uri = intent.getData();
mSending = false;
boolean sendNextMsg = intent.getBooleanExtra(EXTRA_MESSAGE_SENT_SEND_NEXT, false); if (LogTag.DEBUG_SEND) {
Log.v(TAG, "handleSmsSent uri: " + uri + " sendNextMsg: " + sendNextMsg +
" mResultCode: " + mResultCode +
" = " + translateResultCode(mResultCode) + " error: " + error);
} if (mResultCode == Activity.RESULT_OK) {
if (LogTag.DEBUG_SEND || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "handleSmsSent move message to sent folder uri: " + uri);
}
if (!Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_SENT, error)) {
Log.e(TAG, "handleSmsSent: failed to move message " + uri + " to sent folder");
}
if (sendNextMsg) {
sendFirstQueuedMessage();
} // Update the notification for failed messages since they may be deleted.
MessagingNotification.updateSendFailedNotification(this);
} else if ((mResultCode == SmsManager.RESULT_ERROR_RADIO_OFF) ||
(mResultCode == SmsManager.RESULT_ERROR_NO_SERVICE)) {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "handleSmsSent: no service, queuing message w/ uri: " + uri);
}
// We got an error with no service or no radio. Register for state changes so
// when the status of the connection/radio changes, we can try to send the
// queued up messages.
registerForServiceStateChanges();
// We couldn't send the message, put in the queue to retry later.
Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_QUEUED, error);
mToastHandler.post(new Runnable() {
public void run() {
Toast.makeText(SmsReceiverService.this, getString(R.string.message_queued),
Toast.LENGTH_SHORT).show();
}
});
} else if (mResultCode == SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE) {
mToastHandler.post(new Runnable() {
public void run() {
Toast.makeText(SmsReceiverService.this, getString(R.string.fdn_check_failure),
Toast.LENGTH_SHORT).show();
}
});
} else {
messageFailedToSend(uri, error);
if (sendNextMsg) {
sendFirstQueuedMessage();
}
}
}

主要是根据ResultCode进行一些逻辑处理,比如如果发送成功,那么会首先更新短信由待发送变为已发送状态,并且更新Notification等。

如果需要发送报告,那么相关的逻辑大体相似,不再详细介绍。

Ps:发送成功,回调会从moderm那里带回来一个MessageRef,这是一个int值,很重要噢。

短信发送framework层逻辑大体介绍到这里,framework层的短彩信逻辑还有好多,比如接收信息的流程,或者接收长短信的流程等,不再一一介绍了。