Android发送短信的接口可以认为是SmsManager,当然并不是所有的App都可以发送短信的,必须配置相关的权限。App中可以通过SmsManager.getDefault()得到SmsManager的单例。首先来SmsManager主要提供的接口有哪些。
public static SmsManager
getDefault()获取 SmsManager 的默认实例
public void
sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
sendTextMessageInternal(destinationAddress, scAddress, text,
sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/);
}发送一个基于 SMS 的文本
public void
sendDataMessage(
String destinationAddress, String scAddress, short destinationPort,
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)
发送一个基于 SMS 的数据到指定的应用程序端口
public ArrayList<String>
divideMessage(String text) 当短信超过 SMS 消息的最大长度时,将短信分割为几块。
public void
sendMultipartTextMessage(
String destinationAddress, String scAddress, ArrayList<String> parts,
ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) 发送一个基于SMS的多部分文本,调用者应用已经通过调用divideMessage (String text)将消息分割成正确的大小
我们知道短信有长短信和普通短信的区别。长短信一般需要做分隔处理,而普通短信则不需要。这里重点研究的是普通短信。
普通短信的发送接口为SmsManager. sendTextMessage()。其函数原型和参数解释如下:
public void sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent,
PendingIntent deliveryIntent)
destinationAddress: 收件人地址 scAddress: 短信中心号码,null为默认中心号码 sentIntent: 当消息发出时,成功或者失败的信息报告通过PendingIntent来广播。如果该参数为空,则发信程序会被所有位置程序检查一遍,这样会导致发送时间延长。 deliveryIntent: 当消息发送到收件人时,该PendingIntent会被广播。pdu数据在状态报告的extended data ("pdu")中。
下面是SmsManager. sendTextMessage()的具体实现。
public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
sendTextMessageInternal(destinationAddress, scAddress, text,
sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/);
}
可以看到仅仅是转调了内部方法sendTextMessageInternal()。
private void sendTextMessageInternal(String destinationAddress, String scAddress,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForCarrierApp) {
if (TextUtils.isEmpty(destinationAddress)) {//对目的地址进行非空判断
throw new IllegalArgumentException("Invalid destinationAddress");
}
if (TextUtils.isEmpty(text)) {//对发送的内容进行非空判断
throw new IllegalArgumentException("Invalid message body");
}
try {
ISms iccISms = getISmsServiceOrThrow();//获取isim服务
iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),
destinationAddress,
scAddress, text, sentIntent, deliveryIntent,
persistMessageForCarrierApp);
} catch (RemoteException ex) {
// ignore it
}
}
sendTextMessageInternal()首先是获取了isms系统服务,然后调用了其sendTextForSubscriber()方法。这里可以看出Andorid的一贯风格:App总是将某个任务交给有能力完成该任务的服务去执行。而这里这个有能力完成短信发送任务的系统服务其实就是UiccSmsController(从其构造方法可以看出来它就是isim service),因此进入UiccSmsController.sendTextForSubscriber()。
public void sendTextForSubscriber(int subId, String callingPackage, String destAddr,
String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,
deliveryIntent, persistMessageForNonDefaultSmsApp);
} else {
Rlog.e(LOG_TAG,"sendTextForSubscriber iccSmsIntMgr is null for" +
" Subscription: " + subId);
sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
}
}
首先是通过subId获得对应的IccSmsInterfaceManager,然后调用了其sendText方法。里的subId是调用者SmsManager以参数的形式传进来的。通过getSubscriptionId()获取,其主要的逻辑就是如果设置成了默认方式,那就返回默认卡的subId。如果设置成了默认方式但并没有设置默认卡,则发intent提示用户设置。如果没有设置为默认方式,而是通过SmsManager的构造参数传递进来的,则直接返回这个参数的值就行了。还有一种可能就是非默认,且SmsManager又是通过getDegault的方式得到的,那这个subId就可能会根据时间变化了,并且可能返回负数。我们不管这里如何得到卡的subId,直接进入ICCSmsInterface.sendText()。
public void sendText(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp) {
//检查是否声明了发短信的权限
mPhone.getContext().enforceCallingPermission(
Manifest.permission.SEND_SMS,
"Sending SMS message");
sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
persistMessageForNonDefaultSmsApp);//转调了内部方法
}
转调了内部方法sendTextInternal()。
private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp) {
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
" text='"+ text + "' sentIntent=" +
sentIntent + " deliveryIntent=" + deliveryIntent);
}
//检查该操作是否被用户允许
if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return;
}
if (!persistMessageForNonDefaultSmsApp) {
// Only allow carrier app to skip auto message persistence.
enforceCarrierPrivilege();
}
destAddr = filterDestAddress(destAddr);//对目的地址进行检测
mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
}
其实就是对短信的权限和目的地址的有效性进行了筛查。然后进行短信的Dispatch。
SmsDispatcher总共派生出三个子类:CdmaSMSDispatcher、GsmSMSDispatcher、ImsSmsDispatcher,在IccSmsInterfaceManager创建时只创建ImsSMSDispatcher,而在ImsSmsDispatcher创建过程中会对创建其他两种制式的SmsDispatcher,IccSmsInterfaceManager把请求发送给ImsSMSDispatcher后,由ImsSMSDispatcher根据当前网络状态选择使用CdmaSmsDispatcher还是GsmSmsDispatcher。这里主要以Cdma为例。因此调用的是CdmaSmsDispathcer.sendText()。
protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
boolean persistMessage) {
//将短信内容包装成pdu
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null), null);
if (pdu != null) {
//接着将短信包装成tracker
HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),messageUri, false /*isExpectMore*/, text, true /*isText*/, persistMessage);
//carrier是运营商的意思,因此这里和运营商有关
String carrierPackage = getCarrierAppPackageName();
if (carrierPackage != null) {//通过运行商的app发送短信
Rlog.d(TAG, "Found carrier package.");
TextSmsSender smsSender = new TextSmsSender(tracker);
smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
} else {
Rlog.v(TAG, "No carrier package.");
sendSubmitPdu(tracker);//一般走这里
}
} else {
Rlog.e(TAG, "CdmaSMSDispatcher.sendText(): getSubmitPdu() returned null");
if (sentIntent != null) {
try {
sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
} catch (CanceledException ex) {
Rlog.e(TAG, "Intent has been canceled!");
}
}
}
}
进入sendSubmitPdu()
protected void sendSubmitPdu(SmsTracker tracker) {
//紧急回拨模式检测
if (SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false)) {
if (VDBG) {
Rlog.d(TAG, "Block SMS in Emergency Callback mode");
}
tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
return;
}
sendRawPdu(tracker);//这里
}
进入sendRawPdu()
protected void sendRawPdu(SmsTracker tracker) {
HashMap map = tracker.mData; //从tracker中解析出map
byte pdu[] = (byte[]) map.get("pdu");//从map中解析出pdu
if (mSmsSendDisabled) {//短信发送被禁止了
Rlog.e(TAG, "Device does not support sending sms.");
tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
return;
}
if (pdu == null) {//pdu空
Rlog.e(TAG, "Empty PDU");
tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/);
return;
}
// 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.
Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS");
tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
return;
}
// Get package info via packagemanager
PackageInfo appInfo;
try {
// XXX this is lossy- apps can share a UID
appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
} catch (PackageManager.NameNotFoundException e) {
Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS");
tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
return;
}
// 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.
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;
}
sendSms(tracker);//这里
}
}
进入sendSms()。
protected void sendSms(SmsTracker tracker) {
HashMap<String, Object> map = tracker.mData;
// byte[] smsc = (byte[]) map.get("smsc"); // unused for CDMA
byte[] pdu = (byte[]) map.get("pdu");//再次从tracker中解出pdu
Rlog.d(TAG, "sendSms: "
+ " isIms()=" + isIms()
+ " mRetryCount=" + tracker.mRetryCount
+ " mImsRetry=" + tracker.mImsRetry
+ " mMessageRef=" + tracker.mMessageRef
+ " SS=" + mPhone.getServiceState().getState());
sendSmsByPstn(tracker);//这里
}
进入sendSmsByPstn().
protected void sendSmsByPstn(SmsTracker tracker) {
int ss = mPhone.getServiceState().getState();//获取phone状态
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);
return;
}
//获取一个发送完成的Message,一遍发送完成后回到HandleMessage
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
byte[] pdu = (byte[]) tracker.mData.get("pdu");//又解出pdu,好频繁啊
int currentDataNetwork = mPhone.getServiceState().getDataNetworkType();
boolean imsSmsDisabled = (currentDataNetwork == TelephonyManager.NETWORK_TYPE_EHRPD
|| (currentDataNetwork == TelephonyManager.NETWORK_TYPE_LTE
&& !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()))
&& mPhone.getServiceState().getVoiceNetworkType()
== TelephonyManager.NETWORK_TYPE_1xRTT
&& ((CDMAPhone) mPhone).mCT.mState != PhoneConstants.State.IDLE;
// sms over cdma is used:
// if sms over IMS is not supported AND
// this is not a retry case after sms over IMS failed
// indicated by mImsRetry > 0
//注意携带了参数reply是一个EVENT_SEND_SMS_COMPLETE, tracker的消息
if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled) {
mCi.sendCdmaSms(pdu, reply);
} else {
mCi.sendImsCdmaSms(pdu, tracker.mImsRetry, tracker.mMessageRef, reply);
// increment it here, so in case of SMS_FAIL_RETRY over IMS
// next retry will be sent using IMS request again.
tracker.mImsRetry++;//如果是重试,重试次数加1
}
}
mCi我们在phone应用的分析中重点分析过,其实就是RILJ。因此进入到了RIL层。关于RIL在phone应用的分析中已经很详细了,肯定是先构造一个RILRequest,然后将pdu数据写入,接着send(rr),接着在processSolicited中处理发送结果,并调用rr.mResult.sendToTarget()将结果上传到到上层(这里是smsDispatcher)。这里对于RILJ以下的处理过程就不赘述了。
当RILJ发送完毕,reply消息被发送,因此sms发送成功的消息被smsDispather接收并HandleMessage。在handleMessage()中直接调用了handleSendComplete()方法。
protected void handleSendComplete(AsyncResult ar) {
SmsTracker tracker = (SmsTracker) ar.userObj;//从ar中解出tracker
PendingIntent sentIntent = tracker.mSentIntent;//从tracker中解出sendIntent
if (ar.result != null) {//解出回应消息
tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef;
} else {
Rlog.d(TAG, "SmsResponse was null");
}
if (ar.exception == null) {//如果没有异常,表示发送成功
if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent);
//如果需要等待对方接受的结果状态,将tracker添加到pendinglist以等待结果
//需要注意的是这里的mSendItent和mDeliveryIntent都是pendingIntent,就是留待以后触发的意思,需要触发是调用PendingIntent.send()-—网络总结
if (tracker.mDeliveryIntent != null) {
// Expecting a status report. Add it to the list.
deliveryPendingList.add(tracker);//留待以后触发
}
tracker.onSent(mContext);//发送消息广播,内部调用了PendingIntent.send()
} else {//如果有异常,表示发送短信失败
if (DBG) Rlog.d(TAG, "SMS send failed");
//首先获取短信的状态
int ss = mPhone.getServiceState().getState();
//短信发送失败,可以重试,但服务不再Service状态,直接将重试次数设到超过最大
if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
// This is retry after failure over IMS but voice is not available.
// Set retry to max allowed, so no retry is sent and
// cause RESULT_ERROR_GENERIC_FAILURE to be returned to app.
tracker.mRetryCount = MAX_SEND_RETRIES;//设置最大重试次数,即不重试
Rlog.d(TAG, "handleSendComplete: Skipping retry: "
+" isIms()="+isIms()
+" mRetryCount="+tracker.mRetryCount
+" mImsRetry="+tracker.mImsRetry
+" mMessageRef="+tracker.mMessageRef
+" SS= "+mPhone.getServiceState().getState());
}
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);
} else if ((((CommandException)(ar.exception)).getCommandError()
== CommandException.Error.SMS_FAIL_RETRY) &&
tracker.mRetryCount < MAX_SEND_RETRIES) {
//发送失败,重试,从这里看出重试次数有次数限制
tracker.mRetryCount++;
Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
} else {
int errorCode = 0;//默认没有errorcode
if (ar.result != null) {//根据ar设置errorcode
errorCode = ((SmsResponse)ar.result).mErrorCode;
}
int error = RESULT_ERROR_GENERIC_FAILURE;//默认错误为这个
if (((CommandException)(ar.exception)).getCommandError()
== CommandException.Error.FDN_CHECK_FAILURE) {
//如果底层特殊上报了error,则根据底层设置error
error = RESULT_ERROR_FDN_CHECK_FAILURE;
}
//将错误及错误code发送到上层,内部同样调用了PendingIntent.send()触发执行Intent
tracker.onFailed(mContext, error, errorCode);
}
}
}
可以看到对于短信的发送失败和成功状态的处理,最后都是通过SmsTracker来处理的。总结RILJ之上的短信发送过程如下图所示。
这里主要涉及到了PendingIntent,因此还有待研究!
下图是普通短信的处理流程。可以看到的是,在上层,短信是通过源目地址以及String等体现出来的,接着往底层走是tracker,再接着到RILJ演变成了pdu数据,再到RILRequest下发到RILD。
在图中也画出了长短信的处理流程,可以看到长短信的处理与普通短信的处理基本类似,仅仅是多了分段处理(在App中)。
原文地址: http://blog.csdn.net/a34140974/article/details/50964080