Android Mms短信的发送流程,短信发送源码解析

编程入门 行业动态 更新时间:2024-10-22 07:30:55

Android Mms短信的发送流程,短信发送<a href=https://www.elefans.com/category/jswz/34/1770099.html style=源码解析"/>

Android Mms短信的发送流程,短信发送源码解析

  • 发送前的校验

从短信的点击按钮开始着手:

// packages/apps/Mms/src/com/android/mms/ui/ComposeMessageActivity.java@Overridepublic void onClick(View v) {mIsRTL = (v.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);if ((v == mSendButtonSms || v == mSendButtonMms) && isPreparedForSending()) {... ...if (mShowTwoButtons) {confirmSendMessageIfNeeded(SubscriptionManager.getSubId(PhoneConstants.SUB1)[0]);} else {if (mIsRcsEnabled) {if (RcsUtils.isRcsOnline()&& ((mWorkingMessage.getCacheRcsMessage() || (!mWorkingMessage.requiresMms() && mWorkingMessage.hasText())))) {rcsSend();} else {if (mConversation.isGroupChat()) {toast(R.string.rcs_offline_unable_to_send);return;}if (mIsBurnMessage) {toast(R.string.rcs_not_online_can_not_send_burn_message);mIsBurnMessage = false;return;} else {confirmSendMessageIfNeeded();}}} else {confirmSendMessageIfNeeded();}}} ... ...}

首先判断是否是双卡的情况,双卡的情况下通过SubId来区分哪张卡;单卡则判断是否启用RCS(rcs默认是关闭的,配置开关的路径:/Mms/res/values/config.xml config_rcs_sms_version)没有启用会执行confirmSendMessageIfNeeded对收件人地址信息确认.

private void confirmSendMessageIfNeeded() {LogTag.debugD("confirmSendMessageIfNeeded");if (mRcsShareVcard) {mWorkingMessage.setRcsType(RcsUtils.RCS_MSG_TYPE_VCARD);mRcsShareVcard = false;}int slot = SubscriptionManager.getSlotId(SmsManager.getDefault().getDefaultSmsSubscriptionId());if ((TelephonyManager.getDefault().isMultiSimEnabled() &&isLTEOnlyMode(slot))|| (!TelephonyManager.getDefault().isMultiSimEnabled()&& isLTEOnlyMode())) {showDisableLTEOnlyDialog(slot);LogTag.debugD("return for disable LTEOnly");return;}boolean isMms = mWorkingMessage.requiresMms();int defaultSubId = SubscriptionManager.getDefaultSmsSubscriptionId();if (!isRecipientsEditorVisible()) {if (TelephonyManager.getDefault().isMultiSimEnabled()) {if ((TelephonyManager.getDefault().getPhoneCount()) > 1 &&MessageUtils.isMsimIccCardActive()) {if(SmsManager.getDefault().isSMSPromptEnabled()) {launchMsimDialog(true, isMms);} else {sendMsimMessageNotPrompt(true, isMms, defaultSubId);}} else {sendMsimMessageNotPrompt(true, isMms, defaultSubId);}} else if (isMmsWithMobileDataOff(isMms, defaultSubId)) {showMobileDataDisabledDialog();} else {sendMessage(true);}return;}// 判断收件人地址是否有效if (mRecipientsEditor.hasInvalidRecipient(isMms)) {showInvalidRecipientDialog();} else if (TelephonyManager.getDefault().isMultiSimEnabled()) { // 判断是否是双卡的情况if ((TelephonyManager.getDefault().getPhoneCount()) > 1 &&MessageUtils.isMsimIccCardActive()) {if(SmsManager.getDefault().isSMSPromptEnabled()) {launchMsimDialog(true, isMms);} else {sendMsimMessageNotPrompt(true, isMms, defaultSubId);}} else {sendMsimMessageNotPrompt(true, isMms, defaultSubId);}} else if (isMmsWithMobileDataOff(isMms, defaultSubId)) {   // 判断是否是彩信,并且数据流量是否打开showMobileDataDisabledDialog(defaultSubId);} else {if (!TextUtils.isEmpty(getString(R.string.mms_recipient_Limit))&& isMms&& checkForMmsRecipients(getString(R.string.mms_recipient_Limit), true)) {return;}//获取收件人地址,发送// The recipients editor is still open. Make sure we use what's showing there// as the destination.ContactList contacts = mRecipientsEditor.constructContactsFromInput(false);mDebugRecipients = contacts.serialize();sendMessage(true);}}

这里是对收件人地址检测等一些状态判断,最后通过sendMessage()继续发送逻辑.

private void sendMessage(boolean bCheckEcmMode) {LogTag.debugD("sendMessage true");if (mIsRcsEnabled && hasConvertRcsAttachmentToMmsAndSent()) {return;}// Check message size, if >= max message size, do not send message.if(checkMessageSizeExceeded()){LogTag.debugD("MessageSizeExceeded");return;}// 检查当前是否是ECM模式if (bCheckEcmMode) {// TODO: expose this in telephony layer for SDK buildString inEcm = SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE);if (Boolean.parseBoolean(inEcm)) {try {startActivityForResult(new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null),REQUEST_CODE_ECM_EXIT_DIALOG);return;} catch (ActivityNotFoundException e) {// continue to send messageLog.e(TAG, "Cannot find EmergencyCallbackModeExitDialog", e);}}}// Make the recipients editor lost focus, recipients editor will shrink// and filter useless char in recipients to avoid send sms failed.if (isRecipientsEditorVisible()&& mRecipientsEditor.isFocused()&& !mWorkingMessage.requiresMms()) {mTextEditor.requestFocus();}// 判断是否有短信正在发送if (!mSendingMessage) {if (LogTag.SEVERE_WARNING) {String sendingRecipients = mConversation.getRecipients().serialize();if (!sendingRecipients.equals(mDebugRecipients)) {String workingRecipients = mWorkingMessage.getWorkingRecipients();if (workingRecipients != null && !mDebugRecipients.equals(workingRecipients)) {LogTag.warnPossibleRecipientMismatch("ComposeMessageActivity.sendMessage" +" recipients in window: \"" +mDebugRecipients + "\" differ from recipients from conv: \"" +sendingRecipients + "\" and working recipients: " +workingRecipients, this);}}sanityCheckConversation();}// send can change the recipients. Make sure we remove the listeners first and then add// them back once the recipient list has settled.removeRecipientsListeners();// 判断是否为多个联系人,然后进入WorkingMessage执行发送if (mWorkingMessage.getResendMultiRecipients()) {// If resend sms recipient is more than one, use mResendSmsRecipientLogTag.debugD("mWorkingMessage send mResendSmsRecipient=" + mResendSmsRecipient);mWorkingMessage.send(mResendSmsRecipient);} else {LogTag.debugD("mWorkingMessage send mDebugRecipients=" + mDebugRecipients);mWorkingMessage.send(mDebugRecipients);}mSentMessage = true;mSendingMessage = true;addRecipientsListeners();mScrollOnSend = true;   // in the next onQueryComplete, scroll the list to the end.}// But bail out if we are supposed to exit after the message is sent.if (mSendDiscreetMode || MessageUtils.isMailboxMode()) {finish();}}

WorkingMessage包含所有用户编辑短信相关的状态,这里判断如果没有短信正在发送,则通过WorkingMessage发送短信.

/*** Send this message over the network.  Will call back with onMessageSent() once* it has been dispatched to the telephony stack.  This WorkingMessage object is* no longer useful after this method has been called.** @throws ContentRestrictionException if sending an MMS and uaProfUrl is not defined* in mms_config.xml.*/public void send(final String recipientsInUI) {long origThreadId = mConversation.getThreadId();LogTag.debugD("send origThreadId: " + origThreadId);removeSubjectIfEmpty(true /* notify */);// Get ready to write to disk.prepareForSave(true /* notify */);// Make sure the mConversation has RecipientscheckConversationHasRecipients(recipientsInUI);// We need the recipient list for both SMS and MMS.final Conversation conv = mConversation;String msgTxt = mText.toString();if (MmsConfig.isRcsEnabled() && shouldSendMessageWithRcsPolicy()) {sendRcsMessage(recipientsInUI);return;}if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {     // 发送彩信// uaProfUrl setting in mms_config.xml must be present to send an MMS.// However, SMS service will still work in the absence of a uaProfUrl address.if (MmsConfig.getUaProfUrl() == null) {String err = "WorkingMessage.send MMS sending failure. mms_config.xml is " +"missing uaProfUrl setting.  uaProfUrl is required for MMS service, " +"but can be absent for SMS.";RuntimeException ex = new NullPointerException(err);Log.e(TAG, err, ex);// now, let's just crash.throw ex;}// Make local copies of the bits we need for sending a message,// because we will be doing it off of the main thread, which will// immediately continue on to resetting some of this state.final Uri mmsUri = mMessageUri;final PduPersister persister = PduPersister.getPduPersister(mActivity);final SlideshowModel slideshow = mSlideshow;final CharSequence subject = mSubject;final boolean textOnly = mAttachmentType == TEXT;LogTag.debugD("Send mmsUri: " + mmsUri);// 彩信发送线程 // Do the dirty work of sending the message off of the main UI thread.new Thread(new Runnable() {@Overridepublic void run() {final SendReq sendReq = makeSendReq(conv, subject);// Make sure the text in slide 0 is no longer holding onto a reference to// the text in the message text box.slideshow.prepareForSend();sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq, textOnly);updateSendStats(conv);}}, "WorkingMessage.send MMS").start();} else {        //发送短信// Same rules apply as above.// Add user's signature first if this feature is enabled.String text = mText.toString();LogTag.debugD("mText="+text);SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mActivity);if (sp.getBoolean("pref_key_enable_signature", false)) {String signature = (sp.getString("pref_key_edit_signature", "")).trim();if (signature.length() > 0) {String sigBlock = "\n" + signature;if (!text.endsWith(sigBlock)) {// Signature should be written behind the text in a// newline while the signature has changed.text += sigBlock;}}}final String msgText = text;// 短信发送线程new Thread(new Runnable() {@Overridepublic void run() {preSendSmsWorker(conv, msgText, recipientsInUI);updateSendStats(conv);}}, "WorkingMessage.send SMS").start();}// update the Recipient cache with the new to address, if it's differentRecipientIdCache.updateNumbers(conv.getThreadId(), conv.getRecipients());// Mark the message as discarded because it is "off the market" after being sent.mDiscarded = true;}

通过网络发送消息,分别为短信和彩信开启了发送线程

// Message sending stuffprivate void preSendSmsWorker(Conversation conv, String msgText, String recipientsInUI) {mIsSending = true;// If user tries to send the message, it's a signal the inputted text is what they wanted.UserHappinessSignals.userAcceptedImeText(mActivity);// 短信准备发送的回调(这里执行了UI的更新)mStatusListener.onPreMessageSent();long origThreadId = conv.getThreadId();// 获取线程ID(每个收件人对应一个线程ID,threadId也是指一个会话的ID,和sms和thread数据库中thread_id和_id对应)// Make sure we are still using the correct thread ID for our recipient set.long threadId = conv.ensureThreadId();String semiSepRecipients = conv.getRecipients().serialize();// recipientsInUI can be empty when the user types in a number and hits sendif (LogTag.SEVERE_WARNING && ((origThreadId != 0 && origThreadId != threadId) ||((!mResendMultiRecipients && !semiSepRecipients.equals(recipientsInUI)) &&!TextUtils.isEmpty(recipientsInUI)))) {String msg = origThreadId != 0 && origThreadId != threadId ?"WorkingMessage.preSendSmsWorker threadId changed or " +"recipients changed. origThreadId: " +origThreadId + " new threadId: " + threadId +" also mConversation.getThreadId(): " +mConversation.getThreadId():"Recipients in window: \"" +recipientsInUI + "\" differ from recipients from conv: \"" +semiSepRecipients + "\"";// Just interrupt the process of sending message if recipient mismatchLogTag.warnPossibleRecipientMismatch(msg, mActivity);} else {    // 发送短信// just do a regular send. We're already on a non-ui thread so no need to fire// off another thread to do this work.if (mResendMultiRecipients) {sendSmsWorker(msgText, recipientsInUI, threadId);mResendMultiRecipients = false;} else {sendSmsWorker(msgText, semiSepRecipients, threadId);}// 删除短信草稿// Be paranoid and clean any draft SMS up.deleteDraftSmsMessage(threadId);}mIsSending = false;}
  • 拆分多个联系人执行发送
private void sendSmsWorker(String msgText, String semiSepRecipients, long threadId) {// 切割收件人地址String[] dests = TextUtils.split(semiSepRecipients, ";");LogTag.debugD("sendSmsWorker sending message: recipients=" +semiSepRecipients + ", threadId=" + threadId);// 构建MessageSender对象发送短信MessageSender sender = new SmsMessageSender(mActivity, dests, msgText, threadId,mCurrentConvSubId);try {// 拆分联系人,保存到数据库sender.sendMessage(threadId);// Make sure this thread isn't over the limits in message countRecycler.getSmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId);} catch (Exception e) {Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e);}// 短信发送后的回调UI接口(此时短信已被写入数据库,刷新UI信息列表)mStatusListener.onMessageSent();MmsWidgetProvider.notifyDatasetChanged(mActivity);}

创建MessageSender对象发送短信,同时在发送前后都回调发送状态的监听.SmsMessageSender实现MessageSender接口,该接口有三个实现类分别是:

MmsMessageSender: 彩信的发送
SmsSingleRecipientSender: 单个联系人的短信发送
SmsMessageSender: 拆分多个联系人,最终通过SmsSingleRecipientSender发送
public boolean sendMessage(long token) throws MmsException {// In order to send the message one by one, instead of sending now, the message will split,// and be put into the queue along with each destinationsreturn queueMessage(token);}private boolean queueMessage(long token) throws MmsException {if ((mMessageText == null) || (mNumberOfDests == 0)) {// Don't try to send an empty message.if (!(mMessageText == null &&mContext.getResources().getBoolean(R.bool.enable_send_blank_message))) {throw new MmsException("Null message body or dest.");}}SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);boolean requestDeliveryReport = false;if (TelephonyManager.getDefault().isMultiSimEnabled()) {int slotId = SubscriptionManager.getSlotId(mSubId);if (MessageUtils.isMsimIccCardActive()) {requestDeliveryReport = prefs.getBoolean((slotId == PhoneConstants.SUB1) ?MessagingReportsPreferenceActivity.SMS_DELIVERY_REPORT_SUB1 :MessagingReportsPreferenceActivity.SMS_DELIVERY_REPORT_SUB2,DEFAULT_DELIVERY_REPORT_MODE);} else {requestDeliveryReport = prefs.getBoolean((slotId == PhoneConstants.SUB1) ?SmsPreferenceActivity.SMS_DELIVERY_REPORT_SIM1 :SmsPreferenceActivity.SMS_DELIVERY_REPORT_SIM2,DEFAULT_DELIVERY_REPORT_MODE);}} else {requestDeliveryReport = prefs.getBoolean(SmsPreferenceActivity.SMS_DELIVERY_REPORT_NO_MULTI,DEFAULT_DELIVERY_REPORT_MODE);}int priority = -1;try {String priorityStr = PreferenceManager.getDefaultSharedPreferences(mContext).getString("pref_key_sms_cdma_priority", "");priority = Integer.parseInt(priorityStr);} catch (Exception e) {Log.w(TAG, "get priority error:" + e);}// 遍历收件人,加入发送队列for (int i = 0; i < mNumberOfDests; i++) {try {LogTag.debugD("queueMessage mDests[i]: " + mDests[i] + " mThreadId: " + mThreadId);// Check to see whether short message count is up to 2000 for cmccif (MessageUtils.checkIsPhoneMessageFull(mContext)) {break;}log("updating Database with subId = " + mSubId);// 加入发送队列Sms.addMessageToUri(mSubId, mContext.getContentResolver(),Uri.parse("content://sms/queued"), mDests[i],mMessageText, null, mTimestamp,true /* read */,requestDeliveryReport,mThreadId, priority);MessageUtils.updateThreadAttachTypeByThreadId(mContext, mThreadId);} catch (SQLiteException e) {if (LogTag.DEBUG_SEND) {Log.e(TAG, "queueMessage SQLiteException", e);}SqliteWrapper.checkSQLiteException(mContext, e);}}// 通知SmsReceiver,在SmsReceiver中转发带有SmsReceiverService.ACTION_SEND_MESSAGE的intent.Intent intent = new Intent(SmsReceiverService.ACTION_SEND_MESSAGE, null, mContext,SmsReceiver.class);intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mSubId);// Notify the SmsReceiverService to send the message outmContext.sendBroadcast(intent);return false;}

这里主要是分析收件人的个数,然后把信息按每个收件人都放到发送队列数据库中,通过Sms.addMessageToUri( ... ... )保存到content://sms/queued数据库.然后唤起SmsReceiver来处理队列.

SmsMessageSender的sendMessage()返回后WorkingMessage会再次回调UI的接口(mStatusListener.onMessageSent())刷新信息列表.

// packages/apps/Mms/src/com/android/mms/transaction/SmsReceiver.java@Overridepublic void onReceive(Context context, Intent intent) {onReceiveWithPrivilege(context, intent, false);}protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {if (!MessageUtils.hasBasicPermissions()) {Log.d("Mms", "SmsReceiver do not have basic permissions");return;}String action = intent.getAction();LogTag.debugD("onReceiveWithPrivilege:intent="+intent+"|privileged="+privileged);// If 'privileged' is false, it means that the intent was delivered to the base// no-permissions receiver class.  If we get an SMS_RECEIVED message that way, it// means someone has tried to spoof the message by delivering it outside the normal// permission-checked route, so we just ignore it.if (!privileged && (Intents.SMS_DELIVER_ACTION.equals(action) ||"android.cellbroadcastreceiver.CB_AREA_INFO_RECEIVED".equals(action))) {return;}intent.setClass(context, SmsReceiverService.class);intent.putExtra("result", getResultCode());beginStartingService(context, intent);}

SmsReceiver主要负责监听广播,然后将消息转发给SmsReceiverService处理.SmsReceiverService是处理短信收发工作线程,允许我们将传入的消息存储到数据库、更新通知等,而不阻塞SmsReceiver所运行的主线程.

// packages/apps/Mms/src/com/android/mms/transaction/SmsReceiverService.java@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {if (!MmsConfig.isSmsEnabled(this)) {LogTag.debugD("SmsReceiverService: is not the default sms app");// NOTE: We MUST not call stopSelf() directly, since we need to// make sure the wake lock acquired by AlertReceiver is released.SmsReceiver.finishStartingService(SmsReceiverService.this, startId);return Service.START_NOT_STICKY;}// Temporarily removed for this duplicate message track down.int resultCode = intent != null ? intent.getIntExtra("result", 0) : 0;if (resultCode != 0) {LogTag.debugD("onStart: #" + startId + " resultCode: " + resultCode +" = " + translateResultCode(resultCode));}// 处理intent中的信息Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);return Service.START_NOT_STICKY;}

该服务启动后通过mServiceHandler处理intent中的传来的请求

/*** Handle incoming transaction requests.* The incoming requests are initiated by the MMSC Server or by the MMS Client itself.*/@Overridepublic void handleMessage(Message msg) {int serviceId = msg.arg1;Intent intent = (Intent)msg.obj;LogTag.debugD("handleMessage serviceId: " + serviceId + " intent: " + intent);if (intent != null && MmsConfig.isSmsEnabled(getApplicationContext())) {String action = intent.getAction();int error = intent.getIntExtra("errorCode", 0);LogTag.debugD("handleMessage action: " + action + " error: " + error);if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {handleSmsSent(intent, error);} else if (SMS_DELIVER_ACTION.equals(action)) {handleSmsReceived(intent, error);} else if (CB_AREA_INFO_RECEIVED_ACTION.equals(action)) {handleCbSmsReceived(intent, error);} else if (ACTION_BOOT_COMPLETED.equals(action)) {handleBootCompleted();} else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {handleServiceStateChanged(intent);} else if (ACTION_SEND_MESSAGE.endsWith(action)) {// 发送短信handleSendMessage(intent);} else if (ACTION_SEND_INACTIVE_MESSAGE.equals(action)) {handleSendInactiveMessage();}}// NOTE: We MUST not call stopSelf() directly, since we need to// make sure the wake lock acquired by AlertReceiver is released.SmsReceiver.finishStartingService(SmsReceiverService.this, serviceId);}

SmsReceiver转发的消息都由handleMessage来处理.之前发送广播使用的是ACTION_SEND_MESSAGE,因此这里响应处理handleSendMessage方法.

private void handleSendMessage(Intent intent) {int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,SubscriptionManager.getDefaultSmsSubscriptionId());if (mSending.get(subId) == null) {mSending.put(subId, false);}// 判断当前是否有发送任务if (!mSending.get(subId)) {sendFirstQueuedMessage(subId);} else {LogTag.debugD("subId=" + subId + " is in mSending ");}}
// Send first queued message of the given subscriptionpublic synchronized void sendFirstQueuedMessage(int subscription) {boolean success = true;boolean isExpectMore = false;// get all the queued messages from the databasefinal Uri uri = Uri.parse("content://sms/queued");ContentResolver resolver = getContentResolver();String where = "sub_id=?";String[] whereArgs = new String[] {Integer.toString(subscription)};// 从队列中取出短信Cursor c = SqliteWrapper.query(this, resolver, uri,SEND_PROJECTION, where, whereArgs, "date ASC"); // date ASC so we send out// in same order the user// tried to send messages.if (c != null) {try {if (c.moveToFirst()) {      // 发送第一条短信String msgText = c.getString(SEND_COLUMN_BODY);String address = c.getString(SEND_COLUMN_ADDRESS);int threadId = c.getInt(SEND_COLUMN_THREAD_ID);int status = c.getInt(SEND_COLUMN_STATUS);int msgId = c.getInt(SEND_COLUMN_ID);int subId = c.getInt(SEND_COLUMN_SUB_ID);int priority = c.getInt(SEND_COLUMN_PRIORITY);Uri msgUri = ContentUris.withAppendedId(Sms.CONTENT_URI, msgId);// Get the information of is there any messages are pending to process.// If yes, send this inforamtion to framework to control the link and send all// messages on same link based on the support in frameworkif (c.moveToNext()) {isExpectMore = true;}// 通过SmsMessageSender发送单个收件人的短信SmsMessageSender sender = new SmsSingleRecipientSender(this,address, msgText, threadId, status == Sms.STATUS_PENDING,msgUri, subId, isExpectMore);MessageUtils.markAsNotificationThreadIfNeed(this, threadId, address);if(priority != -1){((SmsSingleRecipientSender)sender).setPriority(priority);}LogTag.debugD("sendFirstQueuedMessage " + msgUri +", address: " + address +", threadId: " + threadId);try {// 发送sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);;mSending.put(subscription, true);} catch (MmsException e) {  //如果发送失败,尝试继续发送队列中其他的消息Log.e(TAG, "sendFirstQueuedMessage: failed to send message " + msgUri+ ", caught ", e);mSending.put(subscription, false);messageFailedToSend(msgUri, SmsManager.RESULT_ERROR_GENERIC_FAILURE);success = false;// Sending current message fails. Try to send more pending messages// if there is any.Intent intent = new Intent(SmsReceiverService.ACTION_SEND_MESSAGE, null,this,SmsReceiver.class);intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subscription);sendBroadcast(intent);}}} finally {c.close();}}if (success) {// We successfully sent all the messages in the queue. We don't need to// be notified of any service changes any longer.// In case of MSIM don't unregister service state change if there are any messages// pending for process on other subscriptions. There may be a chance of other// subscription is register and waiting for sevice state changes to process the message.if (!(TelephonyManager.getDefault().getPhoneCount() > 1) ||isUnRegisterAllowed(subscription)) {unRegisterForServiceStateChanges();}}}

这里从数据库队列中读取第一条,通过SmsSingleRecipientSender的sendMessage()方法发送.

  • 区分普通短信和超长短信
// packages/apps/Mms/src/com/android/mms/transaction/SmsSingleRecipientSender.javapublic boolean sendMessage(long token) throws MmsException {if (mMessageText == null) {// Don't try to send an empty message, and destination should be just// one.throw new MmsException("Null message body or have multiple destinations.");}SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(mSubId);ArrayList<String> messages = null;if ((MmsConfig.getEmailGateway() != null) &&(Mms.isEmailAddress(mDest) || MessageUtils.isAlias(mDest))) {// 彩信String msgText;msgText = mDest + " " + mMessageText;mDest = MmsConfig.getEmailGateway();messages = smsManager.divideMessage(msgText);} else {    // 短信messages = smsManager.divideMessage(mMessageText);  // 根据文本内容拆分超长短信// remove spaces and dashes from destination number// (e.g. "801 555 1212" -> "8015551212")// (e.g. "+8211-123-4567" -> "+82111234567")mDest = PhoneNumberUtils.stripSeparators(mDest);mDest = Conversation.verifySingleRecipient(mContext, mThreadId, mDest);mDest = MessageUtils.checkIdp(mContext, mDest, mSubId);}int messageCount = messages.size();if (messageCount == 0) {if (!mContext.getResources().getBoolean(R.bool.enable_send_blank_message)) {// Don't try to send an empty message.throw new MmsException("SmsMessageSender.sendMessage: divideMessage returned " +"empty messages. Original message is \"" + mMessageText + "\"");} else {return sendEmptyMessage();}}boolean moved = Sms.moveMessageToFolder(mContext, mUri, Sms.MESSAGE_TYPE_OUTBOX, 0);if (!moved) {throw new MmsException("SmsMessageSender.sendMessage: couldn't move message " +"to outbox: " + mUri);}ArrayList<PendingIntent> deliveryIntents =  new ArrayList<PendingIntent>(messageCount);ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messageCount);for (int i = 0; i < messageCount; i++) {if (mRequestDeliveryReport && (i == (messageCount - 1))) {  // 为最后一条短信添加送达报告的Intent// TODO: Fix: It should not be necessary to// specify the class in this intent.  Doing that// unnecessarily limits customizability.deliveryIntents.add(PendingIntent.getBroadcast(mContext, 0,new Intent(MessageStatusReceiver.MESSAGE_STATUS_RECEIVED_ACTION,mUri,mContext,MessageStatusReceiver.class),0));} else {deliveryIntents.add(null);}// 拆分后的短信发送完后发送该intent,从而接着发送剩余拆分的短信Intent intent  = new Intent(SmsReceiverService.MESSAGE_SENT_ACTION,mUri,mContext,SmsReceiver.class);intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mSubId);int requestCode = 0;if (i == messageCount -1) { // 最后一条拆分的短信// Changing the requestCode so that a different pending intent// is created for the last fragment with// EXTRA_MESSAGE_SENT_SEND_NEXT set to true.requestCode = 1;intent.putExtra(SmsReceiverService.EXTRA_MESSAGE_SENT_SEND_NEXT, true);}LogTag.debugD("sendMessage sendIntent: " + intent);sentIntents.add(PendingIntent.getBroadcast(mContext, requestCode, intent, 0));}int validityPeriod = getValidityPeriod(mSubId);// Remove all attributes for CDMA international roaming.if (mContext.getResources().getBoolean(R.bool.config_ignore_sms_attributes) &&MessageUtils.isCDMAInternationalRoaming(mSubId)) {Log.v(TAG, "sendMessage during CDMA international roaming.");mPriority = -1;deliveryIntents = null;validityPeriod = -1;}try {// 通过SmsManager发送smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages,sentIntents, deliveryIntents, mPriority, isExpectMore, validityPeriod);} catch (Exception ex) {Log.e(TAG, "SmsMessageSender.sendMessage: caught", ex);throw new MmsException("SmsMessageSender.sendMessage: caught " + ex +" from SmsManager.sendMultipartTextMessage()");}return false;}

这里主要是根据短信的长度调用SmsManager的方法divideMessage()来拆分长短信,添加信息发送状态的广播分别由MessageStatusReceiver监听和SmsReceiverService监听,它们收到广播后,从Intent中获取发送和送达状态,然后更新数据库中信息的状态同时刷新UI.

  • 从Application调用Framework层执行发送
// frameworks/opt/telephony/src/java/android/telephony/SmsManager.javapublic void sendMultipartTextMessage(String destinationAddress, String scAddress, ArrayList<String> parts,ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,int priority, boolean isExpectMore, int validityPeriod) {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 = getISmsServiceOrThrow();if (iccISms != null) {iccISms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),ActivityThread.currentPackageName(), destinationAddress, scAddress,parts, sentIntents, deliveryIntents, priority, isExpectMore,validityPeriod);}} 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, priority, isExpectMore, validityPeriod);}}
  • 发送普通短信
public void sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent, PendingIntent deliveryIntent, int priority,boolean isExpectMore, int validityPeriod) {if (TextUtils.isEmpty(destinationAddress)) {throw new IllegalArgumentException("Invalid destinationAddress");}if (TextUtils.isEmpty(text)) {throw new IllegalArgumentException("Invalid message body");}try {ISms iccISms = getISmsServiceOrThrow();if (iccISms != null) {iccISms.sendTextForSubscriberWithOptions(getSubscriptionId(),ActivityThread.currentPackageName(), destinationAddress, scAddress, text,sentIntent, deliveryIntent, priority, isExpectMore, validityPeriod);}} catch (RemoteException ex) {// ignore it}}

这里调用ISms的实现类UiccSmsController对象的sendTextForSubscriberWithOptions()方法执行发送

// frameworks/opt/telephony/src/java/com/android/internal/telephony/UiccSmsController.javapublic void sendTextForSubscriberWithOptions(int subId, String callingPackage,String destAddr, String scAddr, String parts, PendingIntent sentIntents,PendingIntent deliveryIntents, int priority, boolean isExpectMore,int validityPeriod) {IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);if (iccSmsIntMgr != null ) {iccSmsIntMgr.sendTextWithOptions(callingPackage, destAddr, scAddr, parts, sentIntents,deliveryIntents, priority, isExpectMore, validityPeriod);} else {Rlog.e(LOG_TAG,"sendTextWithOptions iccSmsIntMgr is null for" +" Subscription: " + subId);}}

这里实际又通过IccSmsInterfaceManager来执行发送逻辑.

// frameworks/opt/telephony/src/java/com/android/internal/telephony/IccSmsInterfaceManager.javapublic void sendTextWithOptions(String callingPackage, String destAddr, String scAddr,String text, PendingIntent sentIntent, PendingIntent deliveryIntent,int priority, boolean isExpectMore, int validityPeriod) {// 判断是否声明发送短信权限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 +"validityPeriod" + validityPeriod);}// 是否被用户允许if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),callingPackage) != AppOpsManager.MODE_ALLOWED) {return;}// 继续执行发送逻辑mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,null/*messageUri*/, callingPackage, false /*persistMessage*/, priority,isExpectMore, validityPeriod);}

这里通过mDispatcher继续执行发送逻辑.SMSDispatcher有三个实现类:CdmaSMSDispatcher、GsmSMSDispatcher、ImsSMSDispatcher.ImsSMSDispatcher相当于是对上面两个子类的一个包装,上层调用时主要调用ImsSMSDispatcher中的方法.ImsSMSDispatcher中再去选择调用的是Gsm还是Cdma中的方法

@Overridepublic void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,PendingIntent deliveryIntent, Uri messageUri, String callingPkg,boolean persistMessage, int priority, boolean isExpectMore, int validityPeriod) {Rlog.d(TAG, "sendText");if (isCdmaMo()) {mCdmaDispatcher.sendText(destAddr, scAddr,text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage,priority, isExpectMore, validityPeriod);} else {mGsmDispatcher.sendText(destAddr, scAddr,text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage,priority, isExpectMore, validityPeriod);}}

这里,我们分析GSM网络的情况.

@Overridepublic void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,PendingIntent deliveryIntent, Uri messageUri, String callingPkg,boolean persistMessage, int priority, boolean isExpectMore, int validityPeriod) {// 将短信内容封装成pduSmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddr, destAddr, text, (deliveryIntent != null), validityPeriod);if (pdu != null) {// 将短信封装成trackerHashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),messageUri, isExpectMore, text /*fullMessageText*/,true /*isText*/, validityPeriod, persistMessage, callingPkg);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.");sendRawPdu(tracker);    // 一般走这里}} else {Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");}}
protected void sendRawPdu(SmsTracker tracker) {HashMap map = tracker.getData();         // 从tracker中解析出mapbyte pdu[] = (byte[]) map.get("pdu");       // 从map中解析出pduif (mSmsSendDisabled) {     // 短信发送被禁止了Rlog.e(TAG, "Device does not support sending sms.");tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);return;}if (pdu == null) {Rlog.e(TAG, "Empty PDU");tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/);return;}// Get calling app package name via UID from Binder callPackageManager 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 packagemanagerPackageInfo appInfo;try {// XXX this is lossy- apps can share a UIDappInfo = 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 appif (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) {sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));return;}if (mUsageMonitor.isSmsAuthorizationEnabled()) {final SmsAuthorizationCallback callback = new SmsAuthorizationCallback() {@Overridepublic void onAuthorizationResult(final boolean accepted) {if (accepted) {sendSms(tracker);} else {tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE,SmsUsageMonitor.ERROR_CODE_BLOCKED);}}};mUsageMonitor.authorizeOutgoingSms(tracker.mAppInfo, tracker.mDestAddress,tracker.mFullMessageText, callback, this);} else {sendSms(tracker);   //发送短信}}if (PhoneNumberUtils.isLocalEmergencyNumber(mContext, tracker.mDestAddress)) {new AsyncEmergencyContactNotifier(mContext).execute();}}

这里对要发送的信息及当前环境进行检测,然后进入sendSms()流程

// GsmSMSDispatcher.java@Overrideprotected void sendSms(SmsTracker tracker) {HashMap<String, Object> map = tracker.getData();byte pdu[] = (byte[]) map.get("pdu");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-MRif (((0x01 & pdu[0]) == 0x01)) {pdu[0] |= 0x04; // TP-RDpdu[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());// 发送sendSmsByPstn(tracker);}
@Overrideprotected void sendSmsByPstn(SmsTracker tracker) {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) {tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);return;}HashMap<String, Object> map = tracker.getData();byte smsc[] = (byte[]) map.get("smsc");byte[] pdu = (byte[]) map.get("pdu");//获取带有EVENT_SEND_SMS_COMPLETE的MessageMessage reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);// 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 > 0if (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-MRif (((0x01 & pdu[0]) == 0x01)) {pdu[0] |= 0x04; // TP-RDpdu[1] = (byte) tracker.mMessageRef; // TP-MR}}   //调用RILJ发送if (tracker.mRetryCount == 0 && tracker.mExpectMore) {// 有多条短信在发送mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),IccUtils.bytesToHexString(pdu), reply);} else {    // 正常发送mCi.sendSMS(IccUtils.bytesToHexString(smsc),IccUtils.bytesToHexString(pdu), reply);}} else {    // 通过IMS发送短信if (!isRetryAlwaysOverIMS()) {mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),IccUtils.bytesToHexString(pdu), tracker.mImsRetry,tracker.mMessageRef, reply);} else {mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),IccUtils.bytesToHexString(pdu), 0,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是CommandsInterface接口的实现类RIL.java,因此是调用RIL中去发送短信.这里会携带reply消息,sms发送成功的消息被smsDispather接收,在handleMessage()中调用handleSendComplete()方法.

// frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.javapublic void sendImsGsmSms (String smscPDU, String pdu, int retry, int messageRef,Message result) {RILRequest rr = RILRequest.obtain(RIL_REQUEST_IMS_SEND_SMS, result);rr.mParcel.writeInt(RILConstants.GSM_PHONE);rr.mParcel.writeByte((byte)retry);rr.mParcel.writeInt(messageRef);constructGsmSendSmsRilRequest(rr, smscPDU, pdu);if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));mMetrics.writeRilSendSms(mInstanceId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,SmsSession.Event.Format.SMS_FORMAT_3GPP);send(rr);}

sendSMSExpectMore、sendSMS、sendImsGsmSms、sendImsGsmSms这个四个方法大致相同,都是先构造RILRequest,再调用send方法发送,区别在于传入的请求类型不同,构造出来的pdu结构不同.最后在RILSender处理传过来的RILRequest,通过LocalSocket(LocalSocket其通信方式与Socket差不多,只是LocalSocket没有跨越网络边界)与守护进程RILD通讯(RILD是RIL(Radio Interface Layer) Deamon的简称,简单的说它下面承接GSM/GPRS Modem(电话通信模块),上面接电话应用相关的Java库(telephony internal)).

更多推荐

Android Mms短信的发送流程,短信发送源码解析

本文发布于:2023-06-27 19:25:06,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/915886.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:源码   短信发送   流程   短信   Android

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!