短信发送Framework流程,源代码的版本是4.4
1、通过Mms源代码可以知道短信发送最后调用的关键api如下:
SmsSingleRecipientSender.java
SmsManager smsManager = SmsManager.getDefault();
... try { smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents); } catch (Exception ex) { Log.e(TAG, "SmsMessageSender.sendMessage: caught", ex); throw new MmsException("SmsMessageSender.sendMessage: caught " + ex + " from SmsManager.sendTextMessage()"); } ...
2、SmsManager源代码路径是frameworks/opt/telephony/src/java/android/telephony/gsm/SmsManager.java
SmsManager这里用了单例模式初始化
/** Singleton object constructed during class initialization. */ private static final SmsManager sInstance = new SmsManager();
/** * Get the default instance of the SmsManager * * @return the default instance of the SmsManager */ public static SmsManager getDefault() { return sInstance; }sendMultipartTextMessage的实现如下:
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(ActivityThread.currentPackageName(), 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); } }
如果短信的内容被划分成了多个部分,将进入icc.sendMultipartText(),即如下
try { ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); if (iccISms != null) { iccISms.sendMultipartText(ActivityThread.currentPackageName(), destinationAddress, scAddress, parts, sentIntents, deliveryIntents); } } catch (RemoteException ex) { // ignore it }如果短信的内容只有一部分,则继续调用sendTextMessage(),即如下
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);
下面的流程主要针对sendTextMessage( )分析,sendMultipartText到此为止。
划分的具体实现对应smsManager.divideMessage(mMsgText)
/** * Divide a message text into several fragments, none bigger than * the maximum SMS message size. * * @param text the original message. Must not be null. * @return an <code>ArrayList</code> of strings that, in order, * comprise the original message * * @throws IllegalArgumentException if text is null */ public ArrayList<String> divideMessage(String text) { if (null == text) { throw new IllegalArgumentException("text is null"); } return SmsMessage.fragmentText(text); }
很明显这里调用SmsMessage.fragmentText()
SmsMessage的路径是frameworks/opt/telephony/src/java/android/telephony/SmsMessage.javafragmentText的具体实现如下:
/** * Divide a message text into several fragments, none bigger than * the maximum SMS message text size. * * @param text text, must not be null. * @return an <code>ArrayList</code> of strings that, in order, * comprise the original msg text * * @hide */ public static ArrayList<String> fragmentText(String text) { // This function is for MO SMS TextEncodingDetails ted = (useCdmaFormatForMoSms()) ? com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false) : com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false); // TODO(cleanup): The code here could be rolled into the logic // below cleanly if these MAX_* constants were defined more // flexibly... int limit; if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) { int udhLength; if (ted.languageTable != 0 && ted.languageShiftTable != 0) { udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES; } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) { udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE; } else { udhLength = 0; } if (ted.msgCount > 1) { udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE; } if (udhLength != 0) { udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH; } limit = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength; } else { if (ted.msgCount > 1) { limit = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER; } else { limit = SmsConstants.MAX_USER_DATA_BYTES; } } int pos = 0; // Index in code units. int textLen = text.length(); ArrayList<String> result = new ArrayList<String>(ted.msgCount); while (pos < textLen) { int nextPos = 0; // Counts code units. if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) { if (useCdmaFormatForMoSms() && ted.msgCount == 1) { // For a singleton CDMA message, the encoding must be ASCII... nextPos = pos + Math.min(limit, textLen - pos); } else { // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode). nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit, ted.languageTable, ted.languageShiftTable); } } else { // Assume unicode. nextPos = pos + Math.min(limit / 2, textLen - pos); } if ((nextPos <= pos) || (nextPos > textLen)) { Rlog.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " + nextPos + " >= " + textLen + ")"); break; } result.add(text.substring(pos, nextPos)); pos = nextPos; } return result; }
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(ActivityThread.currentPackageName(), destinationAddress, scAddress, text, sentIntent, deliveryIntent); } } catch (RemoteException ex) { // ignore it } }这里使用aidl的方式远程调用isms服务进行后续的发送流程,即
iccISms.sendText()
,IccSmsInterfaceManagerProxy的路径是frameworks/opt/telephony/src/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
关于isms服务的注册,它是在初始化phone进程时注册的
public IccSmsInterfaceManagerProxy(Context context, IccSmsInterfaceManager iccSmsInterfaceManager) { this.mContext = context; mIccSmsInterfaceManager = iccSmsInterfaceManager; if(ServiceManager.getService("isms") == null) { ServiceManager.addService("isms", this); } createWakelock(); }
iccISms.sendSms()的实现如下:
@Override public void sendText(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { mContext.enforceCallingPermission( android.Manifest.permission.SEND_SMS, "Sending SMS message"); if (mIccSmsInterfaceManager.isShortSMSCode(destAddr)) { mIccSmsInterfaceManager.sendText(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent); return; } ArrayList<String> parts = new ArrayList<String>(); parts.add(text); ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(); sentIntents.add(sentIntent); ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(); deliveryIntents.add(deliveryIntent); broadcastOutgoingSms(callingPackage, destAddr, scAddr, false, parts, sentIntents, deliveryIntents, -1); }检查发送短信的APP是否有SEND_SMS权限,再调用broadcastOutgoingSms()
private void broadcastOutgoingSms(String callingPackage, String destAddr, String scAddr, boolean multipart, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents, int priority) { Intent broadcast = new Intent(Intent.ACTION_NEW_OUTGOING_SMS); broadcast.putExtra("destAddr", destAddr); broadcast.putExtra("scAddr", scAddr); broadcast.putExtra("multipart", multipart); broadcast.putExtra("callingPackage", callingPackage); broadcast.putExtra("callingUid", android.os.Binder.getCallingUid()); broadcast.putStringArrayListExtra("parts", parts); broadcast.putParcelableArrayListExtra("sentIntents", sentIntents); broadcast.putParcelableArrayListExtra("deliveryIntents", deliveryIntents); broadcast.putExtra("priority", priority); mContext.sendOrderedBroadcastAsUser(broadcast, UserHandle.OWNER, android.Manifest.permission.INTERCEPT_SMS, mReceiver, null, Activity.RESULT_OK, destAddr, null); }
private BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { // check if the message was aborted if (getResultCode() != Activity.RESULT_OK) { return; } String destAddr = getResultData(); String scAddr = intent.getStringExtra("scAddr"); String callingPackage = intent.getStringExtra("callingPackage"); ArrayList<String> parts = intent.getStringArrayListExtra("parts"); ArrayList<PendingIntent> sentIntents = intent.getParcelableArrayListExtra("sentIntents"); ArrayList<PendingIntent> deliveryIntents = intent.getParcelableArrayListExtra("deliveryIntents"); if (intent.getIntExtra("callingUid", 0) != 0) { callingPackage = callingPackage + "\\" + intent.getIntExtra("callingUid", 0); } if (intent.getBooleanExtra("multipart", false)) { mIccSmsInterfaceManager.sendMultipartText(callingPackage, destAddr, scAddr, parts, sentIntents, deliveryIntents); return; } PendingIntent sentIntent = null; if (sentIntents != null && sentIntents.size() > 0) { sentIntent = sentIntents.get(0); } PendingIntent deliveryIntent = null; if (deliveryIntents != null && deliveryIntents.size() > 0) { deliveryIntent = deliveryIntents.get(0); } String text = null; if (parts != null && parts.size() > 0) { text = parts.get(0); } mIccSmsInterfaceManager.sendText(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent); } };这里调用mIccSmsInterfaceManager.sendText()继续发送流程
mIccSmsInterfaceManager对应的类是IccSmsInterfaceManager,路径是
framework/opt/telephony/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
mIccSmsInterfaceManager.sendText()的实现如下:
public void sendText(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { int callingUid = Binder.getCallingUid(); String[] callingParts = callingPackage.split("\\\\"); if (callingUid == android.os.Process.PHONE_UID && callingParts.length > 1) { callingUid = Integer.parseInt(callingParts[1]); } if (Binder.getCallingPid() != android.os.Process.myPid()) { mPhone.getContext().enforceCallingPermission( Manifest.permission.SEND_SMS, "Sending SMS message"); } 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, callingUid, callingParts[0]) != AppOpsManager.MODE_ALLOWED) { return; } mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent); }
mDispatcher对应的类是SmsDispatcher,源代码的路径是
framework/opt/telephony/src/java/com/android/internal/telephony/SMSDispatcher.java
SMSDispatcher是一个abstract类,继承它的子类有CdmaSMSDispatcher、GsmSMSDispatcher和IccSMSDispatcher,
这里的mDispatcher初始化如下:
protected IccSmsInterfaceManager(PhoneBase phone) { mPhone = phone; mContext = phone.getContext(); mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mDispatcher = new ImsSMSDispatcher(phone, phone.mSmsStorageMonitor, phone.mSmsUsageMonitor); }
@Override protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { Rlog.d(TAG, "sendText"); if (isCdmaMo()) { mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent); } else { mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent); } }这里根据sim卡的类型决定调用CdmaSMSDispatcher还是GsmSMSDispatcher,这里选择Gsm继续跟进。
mGsmDispatcher对应的类是GsmSMSDispatcher,源代码路径是
framework/opt/telephony/src/java/com/android/internal/telephony/GsmSMSDispatcher.java
mGsmDispatcher.sendText()的流程如下:
@Override 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) { HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu); SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat()); sendRawPdu(tracker); } else { Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null"); } }
protected void sendRawPdu(SmsTracker tracker) {
...
// 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;
}
int 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) {
handleNotInService(ss, tracker.mSentIntent);
} else {
sendSms(tracker);
}
}
}
这里调用sendSms()继续发送流程,sendSms()是一个abstract方法,所以这里是在子类GsmSMSDispatcher中实现的,代码如下:
@Override 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); if (tracker.mRetryCount > 0) { Rlog.d(TAG, "sendSms: " + " mRetryCount=" + tracker.mRetryCount + " mMessageRef=" + tracker.mMessageRef + " SS=" + mPhone.getServiceState().getState()); // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type // TP-RD (bit 2) is 1 for retry // and TP-MR is set to previously failed sms TP-MR if (((0x01 & pdu[0]) == 0x01)) { pdu[0] |= 0x04; // TP-RD pdu[1] = (byte) tracker.mMessageRef; // TP-MR } } Rlog.d(TAG, "sendSms: " +" isIms()="+isIms() +" mRetryCount="+tracker.mRetryCount +" mImsRetry="+tracker.mImsRetry +" mMessageRef="+tracker.mMessageRef +" SS=" +mPhone.getServiceState().getState()); // sms over gsm 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 if (0 == tracker.mImsRetry && !isIms()) { if (tracker.mRetryCount > 0) { // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type // TP-RD (bit 2) is 1 for retry // and TP-MR is set to previously failed sms TP-MR if (((0x01 & pdu[0]) == 0x01)) { pdu[0] |= 0x04; // TP-RD pdu[1] = (byte) tracker.mMessageRef; // TP-MR } } mCi.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply); } else { mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(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++; } }
mCi对应的类是CommandInterface,CommandInterface是一个Interface,RIL是它的一个子类,
也就是说短信发送在此处会调用RIL.java,执行sendText()方法,代码如下:
public void sendSMS (String smscPDU, String pdu, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result); constructGsmSendSmsRilRequest(rr, smscPDU, pdu); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); }程序交给send(RILRequest req)继续执行,代码如下:
protected void send(RILRequest rr) { Message msg; if (mSocket == null) { rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); return; } msg = mSender.obtainMessage(EVENT_SEND, rr); acquireWakeLock(); msg.sendToTarget(); }接下来RIL会将pdu等信息打包,通过socket发送给下层处理,最终形成at指令发送短信。
如果短信发送成功,还记得在发送时,从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中处理,处理逻辑代码如下:
@Override public void handleMessage(Message msg) { switch (msg.what) { case EVENT_SEND_SMS_COMPLETE: // An outbound SMS has been successfully transferred, or failed. handleSendComplete((AsyncResult) msg.obj); break;handleSendComplete的实现如下:
protected void handleSendComplete(AsyncResult ar) { SmsTracker tracker = (SmsTracker) ar.userObj; PendingIntent sentIntent = tracker.mSentIntent; 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); if (SmsApplication.shouldWriteMessageForPackage( tracker.mAppInfo.applicationInfo.packageName, mContext)) { // Persist it into the SMS database as a sent message // so the user can see it in their default app. tracker.writeSentMessage(mContext); } if (tracker.mDeliveryIntent != null) { // Expecting a status report. Add it to the list. 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 (DBG) Rlog.d(TAG, "SMS send failed"); int ss = mPhone.getServiceState().getState(); 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) { handleNotInService(ss, tracker.mSentIntent); } 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).mErrorCode); } 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的,会进行异常的相应的逻辑处理,大体流程相似,故本处介绍无异常时的流程逻辑。
if (SmsApplication.shouldWriteMessageForPackage( tracker.mAppInfo.applicationInfo.packageName, mContext)) { // Persist it into the SMS database as a sent message // so the user can see it in their default app. tracker.writeSentMessage(mContext); }这里判断发送短信的app是否是默认短信,如果不是则保存该条短信到TelephonyProvider中。
最后会发送一个广播,这个广播的intent是PendingIntent,是在Mms中构造的,SmsReceiverService.java会接受该广播并处理
,比如保存短信的状态,失败或者发送成功
发送流程大概就是这样了。