我们先来看最简单的流程入手分析,即收件人只有一个,而且不是长短信的情况

一、地址有效性检测

当点击发送按钮时,触发onClick事件:

[java] view plaincopy
  1. @ComposeMessageActivity.java
  2. public void onClick(View v) {
  3. if ((v == mSendButtonSms || v == mSendButtonMms) && isPreparedForSending()) {
  4. //确认发送
  5. confirmSendMessageIfNeeded();
  6. } else if ((v == mRecipientsPicker)) {
  7. launchMultiplePhonePicker();
  8. }
  9. }

然后进行收件人地址信息的确认

[java] view plaincopy
  1. private void confirmSendMessageIfNeeded() {
  2. if (!isRecipientsEditorVisible()) {
  3. //当前的收件人编辑框不可见,说明所发送的对象已经存在短信的会话,也说明当前的收件人地址是ok的
  4. sendMessage(true);
  5. return;
  6. }
  7. //判断是否为MMS
  8. boolean isMms = mWorkingMessage.requiresMms();
  9. if (mRecipientsEditor.hasInvalidRecipient(isMms)) {
  10. //当前的收件人列表中包含无效地址
  11. if (mRecipientsEditor.hasValidRecipient(isMms)) {
  12. //当前的收件人列表中有无效地址和有效地址,提示用户是否忽略,只发送有效的地址
  13. String title = getResourcesString(R.string.has_invalid_recipient,
  14. mRecipientsEditor.formatInvalidNumbers(isMms));
  15. new AlertDialog.Builder(this)
  16. .setTitle(title)
  17. .setMessage(R.string.invalid_recipient_message)
  18. .setPositiveButton(R.string.try_to_send,
  19. new SendIgnoreInvalidRecipientListener())
  20. .setNegativeButton(R.string.no, new CancelSendingListener())
  21. .show();
  22. } else {
  23. //当前的收件人列表中的地址全部无效,取消发送,提示用户
  24. new AlertDialog.Builder(this)
  25. .setTitle(R.string.cannot_send_message)
  26. .setMessage(R.string.cannot_send_message_reason)
  27. .setPositiveButton(R.string.yes, new CancelSendingListener())
  28. .show();
  29. }
  30. } else {
  31. //收件人地址都是有效的,发送
  32. ContactList contacts = mRecipientsEditor.constructContactsFromInput(false);
  33. mDebugRecipients = contacts.serialize();
  34. sendMessage(true);
  35. }
  36. }

经过地址有效性检测后,通过sendMessage()继续流程,这里传递的bCheckEcmMode参数代表是否检查当前Phone的状态,处于紧急状态时无法发送短信:

[java] view plaincopy
  1. private void sendMessage(boolean bCheckEcmMode) {
  2. if (bCheckEcmMode) {
  3. //判断当前是否为紧急呼叫模式
  4. String inEcm = SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE);
  5. if (Boolean.parseBoolean(inEcm)) {
  6. try {
  7. //紧急状态下,无法发送短彩信
  8. startActivityForResult( new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null), REQUEST_CODE_ECM_EXIT_DIALOG);
  9. return;
  10. } catch (ActivityNotFoundException e) {
  11. Log.e(TAG, "Cannot find EmergencyCallbackModeExitDialog", e);
  12. }
  13. }
  14. }
  15. //判断当前是否有短信正在发送
  16. if (!mSendingMessage) {
  17. //重置收件人控件的监听器
  18. removeRecipientsListeners();
  19. //进入WorkingMessage处理流程
  20. mWorkingMessage.send(mDebugRecipients);
  21. mSentMessage = true;
  22. mSendingMessage = true;
  23. addRecipientsListeners();
  24. mScrollOnSend = true;   // in the next onQueryComplete, scroll the list to the end.
  25. }
  26. // But bail out if we are supposed to exit after the message is sent.
  27. if (mSendDiscreetMode) {
  28. finish();
  29. }
  30. }

二、创建短彩信的发送线程

在sendMessage()中经过对当前紧急服务的处理,然后判断如果当前没有短信正在发送,则通过WorkingMessage发送短信。这里的WorkingMessage是处理当前所编辑的信息的工具类,没有父类,在ComposeMessageActivity界面被创建时或者短信被发送出去时创建,主要负责区分短彩信的流程以及发送短信时UI的更新

[java] view plaincopy
  1. @WorkingMessage.java
  2. public void send(final String recipientsInUI) {
  3. long origThreadId = mConversation.getThreadId();
  4. removeSubjectIfEmpty(true /* notify */);
  5. prepareForSave(true /* notify */);
  6. //拿到当前的会话
  7. final Conversation conv = mConversation;
  8. String msgTxt = mText.toString();
  9. if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {
  10. //彩信发送
  11. if (MmsConfig.getUaProfUrl() == null) {
  12. String err = "WorkingMessage.send MMS sending failure. mms_config.xml is " +
  13. "missing uaProfUrl setting.  uaProfUrl is required for MMS service, " +
  14. "but can be absent for SMS.";
  15. RuntimeException ex = new NullPointerException(err);
  16. Log.e(TAG, err, ex);
  17. // now, let's just crash.
  18. throw ex;
  19. }
  20. final Uri mmsUri = mMessageUri;
  21. final PduPersister persister = PduPersister.getPduPersister(mActivity);
  22. final SlideshowModel slideshow = mSlideshow;
  23. final CharSequence subject = mSubject;
  24. final boolean textOnly = mAttachmentType == TEXT;
  25. //彩信发送线程
  26. new Thread(new Runnable() {
  27. @Override
  28. public void run() {
  29. final SendReq sendReq = makeSendReq(conv, subject);
  30. slideshow.prepareForSend();
  31. sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq, textOnly);
  32. updateSendStats(conv);
  33. }
  34. }, "WorkingMessage.send MMS").start();
  35. } else {
  36. //短信发送流程
  37. final String msgText = mText.toString();
  38. new Thread(new Runnable() {
  39. @Override
  40. public void run() {
  41. preSendSmsWorker(conv, msgText, recipientsInUI);
  42. updateSendStats(conv);
  43. }
  44. }, "WorkingMessage.send SMS").start();
  45. }
  46. // update the Recipient cache with the new to address, if it's different
  47. RecipientIdCache.updateNumbers(conv.getThreadId(), conv.getRecipients());
  48. // Mark the message as discarded because it is "off the market" after being sent.
  49. mDiscarded = true;
  50. }

在上面的send流程中,WorkingMessage对短彩信进行分类,分别创建子线程进行发送,本节我们只关注短信流程,他是在preSendSmsWorker()中被发送的:

[java] view plaincopy
  1. private void preSendSmsWorker(Conversation conv, String msgText, String recipientsInUI) {
  2. UserHappinessSignals.userAcceptedImeText(mActivity);
  3. //UI刷新
  4. mStatusListener.onPreMessageSent();
  5. //获取初始的线程ID
  6. long origThreadId = conv.getThreadId();
  7. //获取分配的线程ID
  8. long threadId = conv.ensureThreadId();
  9. String semiSepRecipients = conv.getRecipients().serialize();
  10. // recipientsInUI can be empty when the user types in a number and hits send
  11. if (LogTag.SEVERE_WARNING && ((origThreadId != 0 && origThreadId != threadId) || (!semiSepRecipients.equals(recipientsInUI) && !TextUtils.isEmpty(recipientsInUI)))) {
  12. }else {
  13. //发送短信
  14. sendSmsWorker(msgText, semiSepRecipients, threadId);
  15. //删除草稿
  16. deleteDraftSmsMessage(threadId);
  17. }
  18. }

在上面的preSendSmsWorker中进行了四个处理:

            1、更新UI(更新编辑框等控件);

            2、获取当前发送的ThreadID;

            3、发送短信;

            4、删除草稿;

接下来我们继续来看发送的过程:

[java] view plaincopy
  1. private void sendSmsWorker(String msgText, String semiSepRecipients, long threadId) {
  2. //获取当前发送的收件人地址
  3. String[] dests = TextUtils.split(semiSepRecipients, ";");
  4. //获取MessageSender对象,传递进去4个参数分别为:收件人、信息文本、当前的threadId、SimID。
  5. MessageSender sender = new SmsMessageSender(mActivity, dests, msgText, threadId);
  6. try {
  7. //通过MessageSender发送出去
  8. sender.sendMessage(threadId);
  9. // Make sure this thread isn't over the limits in message count
  10. Recycler.getSmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId);
  11. } catch (Exception e) {
  12. Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e);
  13. }
  14. //更新UI
  15. mStatusListener.onMessageSent();
  16. MmsWidgetProvider.notifyDatasetChanged(mActivity);
  17. }

经过上面WorkingMessage的sendSmsWorker过程,创建了MessageSender对象,并通过该对象的sendMessage()方法将信息发送出去,并在发送之后再次更新UI界面(正在发送中)。

三、通过SmsMessageSender拆分多个收件人

上面创建的MessageSender对象,继承自MessageSender接口,主要方法只有三个:

[java] view plaincopy
  1. public boolean sendMessage(long token) throws MmsException {};
  2. private boolean queueMessage(long token) throws MmsException {};
  3. private String getOutgoingServiceCenter(long threadId) {};

他的主要作用就是对当前收件人信息拆分后,把群发的短信构建成一个短信队列并保存在数据库中,然后通知SmsReceiverService将队列读取出来并发送出去。
        下面来看其具体实现过程。

[java] view plaincopy
  1. @SmsMessageSender.java
  2. public boolean sendMessage(long token) throws MmsException {
  3. return queueMessage(token);
  4. }
  5. private boolean queueMessage(long token) throws MmsException {
  6. if ((mMessageText == null) || (mNumberOfDests == 0)) {
  7. //空信息不能发送
  8. throw new MmsException("Null message body or dest.");
  9. }
  10. SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
  11. boolean requestDeliveryReport = prefs.getBoolean(
  12. MessagingPreferenceActivity.SMS_DELIVERY_REPORT_MODE,
  13. DEFAULT_DELIVERY_REPORT_MODE);
  14. //根据当前短信的收件人数目,遍历当前的发送队列
  15. for (int i = 0; i < mNumberOfDests; i++) {
  16. try {
  17. //将当前要发送的短消息放入发送队列中
  18. Sms.addMessageToUri(mContext.getContentResolver(),
  19. Uri.parse("content://sms/queued"), mDests[i],
  20. mMessageText, null, mTimestamp,
  21. true /* read */,
  22. requestDeliveryReport,
  23. mThreadId);
  24. } catch (SQLiteException e) {
  25. if (LogTag.DEBUG_SEND) {
  26. Log.e(TAG, "queueMessage SQLiteException", e);
  27. }
  28. SqliteWrapper.checkSQLiteException(mContext, e);
  29. }
  30. }
  31. //通知SmsReceiverService发送短信
  32. mContext.sendBroadcast(new Intent(SmsReceiverService.ACTION_SEND_MESSAGE,
  33. null,
  34. mContext,
  35. SmsReceiver.class));
  36. return false;
  37. }

在上面这个过程中,将收件人地址拆分后,生成一个短信队列放入"content://sms/queued"中,然后给SmsReceiver发送了通知,这里的SmsReceiver作用仅仅是将该通知转发给SmsReceiverService而已:

[java] view plaincopy
  1. @SmsReceiver.java
  2. public void onReceive(Context context, Intent intent) {
  3. onReceiveWithPrivilege(context, intent, false);
  4. }
  5. protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
  6. if (!privileged && intent.getAction().equals(Intents.SMS_DELIVER_ACTION)) {
  7. return;
  8. }
  9. //将请求转交给SmsReceiverService来处理
  10. intent.setClass(context, SmsReceiverService.class);
  11. intent.putExtra("result", getResultCode());
  12. beginStartingService(context, intent);
  13. }

也就是说,SmsMessageSender将要发送的短信放入队列中之后,经过SmsReceiver将该消息发送给了SmsReceiverService, 这里的SmsReceiverService是负责短信的收发的Service。
        下面来看SmsReceiverService对于发送短消息的处理过程。
        这个Service被创建时,会创建一个子线程(HandlerThread)以及该线程的Handler对象(ServiceHandler):

[java] view plaincopy
  1. @SmsReceiverService.java
  2. public void onCreate() {
  3. //创建子线程处理各种消息
  4. HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
  5. thread.start();
  6. mServiceLooper = thread.getLooper();
  7. mServiceHandler = new ServiceHandler(mServiceLooper);
  8. }

当该Service被启动时,就会对当前的请求进行处理:

[java] view plaincopy
  1. public int onStartCommand(Intent intent, int flags, int startId) {
  2. mResultCode = intent != null ? intent.getIntExtra("result", 0) : 0;
  3. //通知Handler处理当前的请求
  4. Message msg = mServiceHandler.obtainMessage();
  5. msg.arg1 = startId;
  6. msg.obj = intent;
  7. mServiceHandler.sendMessage(msg);
  8. return Service.START_NOT_STICKY;
  9. }

然后看当前Handler的处理:

[java] view plaincopy
  1. private final class ServiceHandler extends Handler {
  2. public ServiceHandler(Looper looper) {
  3. super(looper);
  4. }
  5. @Override
  6. public void handleMessage(Message msg) {
  7. int serviceId = msg.arg1;
  8. Intent intent = (Intent)msg.obj;
  9. if (intent != null && MmsConfig.isSmsEnabled(getApplicationContext())) {
  10. String action = intent.getAction();
  11. int error = intent.getIntExtra("errorCode", 0);
  12. if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {
  13. handleSmsSent(intent, error);
  14. } else if (SMS_DELIVER_ACTION.equals(action)) {
  15. handleSmsReceived(intent, error);
  16. } else if (ACTION_BOOT_COMPLETED.equals(action)) {
  17. handleBootCompleted();
  18. } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
  19. handleServiceStateChanged(intent);
  20. } else if (ACTION_SEND_MESSAGE.endsWith(action)) {
  21. //发送短信
  22. handleSendMessage();
  23. } else if (ACTION_SEND_INACTIVE_MESSAGE.equals(action)) {
  24. handleSendInactiveMessage();
  25. }
  26. }
  27. SmsReceiver.finishStartingService(SmsReceiverService.this, serviceId);
  28. }
  29. }

从上面的case分支中我们看到,对于当前的发送短信的请求(ACTION_SEND_MESSAGE)将会通过handleSendMessage()来处理:

[java] view plaincopy
  1. private void handleSendMessage() {
  2. if (!mSending) {
  3. //当前没有其他任务时就触发发送的操作
  4. sendFirstQueuedMessage();
  5. }
  6. }
  7. public synchronized void sendFirstQueuedMessage() {
  8. boolean success = true;
  9. final Uri uri = Uri.parse("content://sms/queued");
  10. ContentResolver resolver = getContentResolver();
  11. //从队列中拿到要发送的短信
  12. Cursor c = SqliteWrapper.query(this, resolver, uri, SEND_PROJECTION, null, null, "date ASC");
  13. if (c != null) {
  14. try {
  15. //发送队列中第一条短信
  16. if (c.moveToFirst()) {
  17. String msgText = c.getString(SEND_COLUMN_BODY);
  18. String address = c.getString(SEND_COLUMN_ADDRESS);
  19. int threadId = c.getInt(SEND_COLUMN_THREAD_ID);
  20. int status = c.getInt(SEND_COLUMN_STATUS);
  21. int msgId = c.getInt(SEND_COLUMN_ID);
  22. Uri msgUri = ContentUris.withAppendedId(Sms.CONTENT_URI, msgId);
  23. //构建SmsSingleRecipientSender对象
  24. SmsMessageSender sender = new SmsSingleRecipientSender(this,
  25. address, msgText, threadId, status == Sms.STATUS_PENDING,
  26. msgUri);
  27. try {
  28. //发送
  29. sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);;
  30. mSending = true;
  31. } catch (MmsException e) {
  32. mSending = false;
  33. messageFailedToSend(msgUri, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
  34. success = false;
  35. sendBroadcast(new Intent(SmsReceiverService.ACTION_SEND_MESSAGE,
  36. null,
  37. this,
  38. SmsReceiver.class));
  39. }
  40. }
  41. } finally {
  42. c.close();
  43. }
  44. }
  45. if (success) {
  46. unRegisterForServiceStateChanges();
  47. }
  48. }

我们看到,经过SmsReceiverService中Handler的处理,将数据库中的当前要发送的短信队列取出来,然后取出队列中第一个短消息任务,通过SmsSingleRecipientSender的sendMessage()方法发送出去。至此SmsReceiverService的流程就走完了,他的作用主要就是拿到队列中的第一条短消息,构建SmsSingleRecipientSender对象并发送出去

四、通过SmsSingleRecipientSender拆分长短信

SmsSingleRecipientSender类继承自SmsMessageSender,他所提供的方法只有一个:

[java] view plaincopy
  1. public boolean sendMessage(long token) throws MmsException {};

其处理内容就是对长短消息进行分割。然后注册两个广播(一个用于广播当前正在发送,另一个广播短信的送达状态报告),之后通过SmsManager发送出去。

[java] view plaincopy
  1. @SmsSingleRecipientSender.java
  2. public boolean sendMessage(long token) throws MmsException {
  3. if (mMessageText == null) {
  4. throw new MmsException("Null message body or have multiple destinations.");
  5. }
  6. SmsManager smsManager = SmsManager.getDefault();
  7. ArrayList<String> messages = null;
  8. //拆分长短信
  9. if ((MmsConfig.getEmailGateway() != null) && (Mms.isEmailAddress(mDest) || MessageUtils.isAlias(mDest))) {
  10. //彩信
  11. String msgText;
  12. msgText = mDest + " " + mMessageText;
  13. mDest = MmsConfig.getEmailGateway();
  14. messages = smsManager.divideMessage(msgText);
  15. } else {
  16. //短信
  17. messages = smsManager.divideMessage(mMessageText);
  18. mDest = PhoneNumberUtils.stripSeparators(mDest);
  19. mDest = Conversation.verifySingleRecipient(mContext, mThreadId, mDest);
  20. }
  21. int messageCount = messages.size();
  22. if (messageCount == 0) {
  23. throw new MmsException("SmsMessageSender.sendMessage: divideMessage returned " + "empty messages. Original message is \"" + mMessageText + "\"");
  24. }
  25. boolean moved = Sms.moveMessageToFolder(mContext, mUri, Sms.MESSAGE_TYPE_OUTBOX, 0);
  26. if (!moved) {
  27. throw new MmsException("SmsMessageSender.sendMessage: couldn't move message " + "to outbox: " + mUri);
  28. }
  29. ArrayList<PendingIntent> deliveryIntents =  new ArrayList<PendingIntent>(messageCount);
  30. ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messageCount);
  31. for (int i = 0; i < messageCount; i++) {
  32. if (mRequestDeliveryReport && (i == (messageCount - 1))) {
  33. //所有短信被发送完毕后,在最后一条短信后面添加送达报告的Intent
  34. deliveryIntents.add(PendingIntent.getBroadcast(
  35. mContext, 0,
  36. new Intent(
  37. MessageStatusReceiver.MESSAGE_STATUS_RECEIVED_ACTION,
  38. mUri,
  39. mContext,
  40. MessageStatusReceiver.class),
  41. 0));
  42. } else {
  43. deliveryIntents.add(null);
  44. }
  45. //对于拆分后的短消息,需要在每条信息发送完毕后发送该Intent,从而接着发送剩下的拆分短信
  46. Intent intent  = new Intent(SmsReceiverService.MESSAGE_SENT_ACTION,
  47. mUri,
  48. mContext,
  49. SmsReceiver.class);
  50. int requestCode = 0;
  51. if (i == messageCount -1) {
  52. //收到该附加数据说明当前的拆分短信已经发送完毕
  53. requestCode = 1;
  54. intent.putExtra(SmsReceiverService.EXTRA_MESSAGE_SENT_SEND_NEXT, true);
  55. }
  56. sentIntents.add(PendingIntent.getBroadcast(mContext, requestCode, intent, 0));
  57. }
  58. try {
  59. //发送
  60. smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents);
  61. } catch (Exception ex) {
  62. throw new MmsException("SmsMessageSender.sendMessage: caught " + ex + " from SmsManager.sendTextMessage()");
  63. }
  64. return false;
  65. }

经过上面的准备过程,在通过SmsManager发送信息之前,还添加了两个Intent:SmsReceiverService.MESSAGE_SENT_ACTION和MessageStatusReceiver.MESSAGE_STATUS_RECEIVED_ACTION。这两个Intent的作用分别是:
        MESSAGE_STATUS_RECEIVED_ACTION:当所有长短信(或短消息)发送完毕后,发送该Intent。
        MESSAGE_SENT_ACTION:分割后的短消息,每发送一条,都会发送该Intent,当最后一条发送完毕后,将会在该Intent中附加EXTRA_MESSAGE_SENT_SEND_NEXT=true的数据
        具体细节在发送完毕后再分析。
        接下来看SmsManager,这里的SmsManager是单例模型,通过其自己的getDefault()或者getSmsManagerForSubscriber()方法就可以得到该对象。

[java] view plaincopy
  1. @SmsManager.java
  2. public void sendMultipartTextMessage( String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
  3. if (TextUtils.isEmpty(destinationAddress)) {
  4. throw new IllegalArgumentException("Invalid destinationAddress");
  5. }
  6. if (parts == null || parts.size() < 1) {
  7. throw new IllegalArgumentException("Invalid message body");
  8. }
  9. if (parts.size() > 1) {
  10. //长短信发送
  11. try {
  12. ISms iccISms = getISmsServiceOrThrow();
  13. iccISms.sendMultipartText(ActivityThread.currentPackageName(),
  14. destinationAddress, scAddress, parts,
  15. sentIntents, deliveryIntents);
  16. } catch (RemoteException ex) {
  17. }
  18. } else {
  19. //普通短信发送
  20. PendingIntent sentIntent = null;
  21. PendingIntent deliveryIntent = null;
  22. if (sentIntents != null && sentIntents.size() > 0) {
  23. sentIntent = sentIntents.get(0);
  24. }
  25. if (deliveryIntents != null && deliveryIntents.size() > 0) {
  26. deliveryIntent = deliveryIntents.get(0);
  27. }
  28. sendTextMessage(destinationAddress, scAddress, parts.get(0),
  29. sentIntent, deliveryIntent);
  30. }
  31. }

在上面的流程中区分了长短信和普通短信的流程,我们目前只分析普通短消息,继续看sendTextMessage():

[java] view plaincopy
  1. public void sendTextMessage( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
  2. if (TextUtils.isEmpty(destinationAddress)) {
  3. throw new IllegalArgumentException("Invalid destinationAddress");
  4. }
  5. if (TextUtils.isEmpty(text)) {
  6. throw new IllegalArgumentException("Invalid message body");
  7. }
  8. try {
  9. //继续
  10. ISms iccISms = getISmsServiceOrThrow();
  11. iccISms.sendText(ActivityThread.currentPackageName(), destinationAddress, scAddress, text, sentIntent, deliveryIntent);
  12. } catch (RemoteException ex) {
  13. }
  14. }

到这里,SmsMessage通过调用iccISms对象的sendText()方法将短信继续传递,而SmsMessage的流程就此结束。

五、发送单条短信

上面的iccISms对象是UiccSmsController的客户端:

[java] view plaincopy
  1. private static ISms getISmsService() {
  2. return ISms.Stub.asInterface(ServiceManager.getService("isms"));
  3. }

所以接下来我们需要进入UiccSmsController的流程中分析:

[java] view plaincopy
  1. @UiccSmsController.java
  2. public void sendText(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
  3. sendTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent);
  4. }
  5. public void sendTextForSubscriber(long subId, String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
  6. IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
  7. if (iccSmsIntMgr != null) {
  8. //通过IccSmsInterfaceManager发送短信
  9. iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent);
  10. } else {
  11. }
  12. }

在上面的UiccSmsController中将任务交给IccSmsInterfaceManager来继续处理。
        IccSmsInterfaceManager这个类没有父类,他在创建PhoneProxy的时候进行初始化,其作用是把请求发送给相应的处理者。比如对于sendText()将会转交给ImsSMSDispatcher来实现。

[java] view plaincopy
  1. @IccSmsInterfaceManager.java
  2. public void sendText(String callingPackage, String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
  3. //是否声明了发短信的权限
  4. mPhone.getContext().enforceCallingPermission( Manifest.permission.SEND_SMS, "Sending SMS message");
  5. //该操作是否被用户允许
  6. if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) {
  7. return;
  8. }
  9. //调用ImsSMSDispatcher发送
  10. destAddr = filterDestAddress(destAddr);
  11. mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, null, callingPackage);
  12. }

在上面这个过程中,先进行两级权限检查,然后通过ImsSMSDispatcher发送信息。
        SMSDispatcher总共派生出三个子类:CdmaSMSDispatcher、GsmSMSDispatcher、ImsSMSDispatcher,在IccSmsInterfaceManager创建时只创建ImsSMSDispatcher,而在ImsSMSDispatcher创建过程中会对创建其他两种制式的SMSDispatcher,IccSmsInterfaceManager把请求发送给ImsSMSDispatcher后,由ImsSMSDispatcher根据当前网络状态选择使用CdmaSMSDispatcher还是GsmSMSDispatcher。

[java] view plaincopy
  1. @ImsSMSDispatcher.java
  2. protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
  3. //根据当前网络类型发送,我们只分析GSM
  4. if (isCdmaMo()) {
  5. mCdmaDispatcher.sendText(destAddr, scAddr,
  6. text, sentIntent, deliveryIntent, messageUri, callingPkg);
  7. } else {
  8. mGsmDispatcher.sendText(destAddr, scAddr,
  9. text, sentIntent, deliveryIntent, messageUri, callingPkg);
  10. }
  11. }

在上面的过程中,根据当前网络环境使用不同的SMSDispatcher,对于GSM网络来说,当前使用的是GsmSMSDispatcher对象。
        这是Framework层与RILJ最接近的对象,他将请求发送给RILJ。

[java] view plaincopy
  1. @GsmSMSDispatcher.java
  2. protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
  3. //对短信内容进行编码
  4. SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( scAddr, destAddr, text, (deliveryIntent != null));
  5. if (pdu != null) {
  6. if (messageUri == null) {
  7. if (SmsApplication.shouldWriteMessageForPackage(callingPkg, mContext)) {
  8. //写入发件箱
  9. messageUri = writeOutboxMessage(
  10. getSubId(),
  11. destAddr,
  12. text,
  13. deliveryIntent != null,
  14. callingPkg);
  15. }
  16. } else {
  17. //移到发件箱
  18. moveToOutbox(getSubId(), messageUri, callingPkg);
  19. }
  20. HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
  21. //发送
  22. SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), messageUri, false);
  23. sendRawPdu(tracker);
  24. } else {
  25. }
  26. }

在上面的过程中,对短消息内容进行编码,然后把短消息写入(或移入)发件箱,然后利用当前的发地址、收地址、文本、附加Intent等信息创建SmsTracker对象,然后调用sendRawPdu(),这个方法是在GsmSMSDispatcher的父类SMSDispatcher中实现的:

[java] view plaincopy
  1. @SMSDispatcher.java
  2. protected void sendRawPdu(SmsTracker tracker) {
  3. HashMap map = tracker.mData;
  4. byte pdu[] = (byte[]) map.get("pdu");
  5. if (mSmsSendDisabled) {
  6. tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
  7. return;
  8. }
  9. if (pdu == null) {
  10. tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/);
  11. return;
  12. }
  13. PackageManager pm = mContext.getPackageManager();
  14. String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
  15. if (packageNames == null || packageNames.length == 0) {
  16. // Refuse to send SMS if we can't get the calling package name.
  17. tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
  18. return;
  19. }
  20. // Get package info via packagemanager
  21. PackageInfo appInfo;
  22. try {
  23. // XXX this is lossy- apps can share a UID
  24. appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
  25. } catch (PackageManager.NameNotFoundException e) {
  26. Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS");
  27. tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
  28. return;
  29. }
  30. // checkDestination() returns true if the destination is not a premium short code or the
  31. // sending app is approved to send to short codes. Otherwise, a message is sent to our
  32. // handler with the SmsTracker to request user confirmation before sending.
  33. if (checkDestination(tracker)) {
  34. // check for excessive outgoing SMS usage by this app
  35. if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) {
  36. sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
  37. return;
  38. }
  39. //发送
  40. sendSms(tracker);
  41. }
  42. }

这里会对要发送的信息以及当前环境进行检测,然后进入sendSms()流程,这个方法又回到了GsmSMSDispatcher中实现:

[java] view plaincopy
  1. @GsmSMSDispatcher.java
  2. protected void sendSms(SmsTracker tracker) {
  3. HashMap<String, Object> map = tracker.mData;
  4. byte pdu[] = (byte[]) map.get("pdu");
  5. if (tracker.mRetryCount > 0) {
  6. // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
  7. //   TP-RD (bit 2) is 1 for retry
  8. //   and TP-MR is set to previously failed sms TP-MR
  9. if (((0x01 & pdu[0]) == 0x01)) {
  10. pdu[0] |= 0x04; // TP-RD
  11. pdu[1] = (byte) tracker.mMessageRef; // TP-MR
  12. }
  13. }
  14. // Send SMS via the carrier app.
  15. BroadcastReceiver resultReceiver = new SMSDispatcherReceiver(tracker);
  16. Intent intent = new Intent(Intents.SMS_SEND_ACTION);
  17. String carrierPackage = getCarrierAppPackageName(intent);
  18. if (carrierPackage != null) {
  19. intent.setPackage(carrierPackage);
  20. intent.putExtra("pdu", pdu);
  21. intent.putExtra("smsc", (byte[]) map.get("smsc"));
  22. intent.putExtra("format", getFormat());
  23. if (tracker.mSmsHeader != null && tracker.mSmsHeader.concatRef != null) {
  24. SmsHeader.ConcatRef concatRef = tracker.mSmsHeader.concatRef;
  25. intent.putExtra("concat.refNumber", concatRef.refNumber);
  26. intent.putExtra("concat.seqNumber", concatRef.seqNumber);
  27. intent.putExtra("concat.msgCount", concatRef.msgCount);
  28. }
  29. intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
  30. Rlog.d(TAG, "Sending SMS by carrier app.");
  31. mContext.sendOrderedBroadcast(intent, android.Manifest.permission.RECEIVE_SMS,
  32. AppOpsManager.OP_RECEIVE_SMS, resultReceiver,
  33. null, Activity.RESULT_CANCELED, null, null);
  34. } else {
  35. //发送
  36. sendSmsByPstn(tracker);
  37. }
  38. }

然后来看sendSmsByPstn():

[java] view plaincopy
  1. protected void sendSmsByPstn(SmsTracker tracker) {
  2. int ss = mPhone.getServiceState().getState();
  3. if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
  4. tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);
  5. return;
  6. }
  7. //拿到SmsTracker中保存的信息
  8. HashMap<String, Object> map = tracker.mData;
  9. byte smsc[] = (byte[]) map.get("smsc");
  10. byte[] pdu = (byte[]) map.get("pdu");
  11. Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
  12. if (0 == tracker.mImsRetry && !isIms()) {
  13. if (tracker.mRetryCount > 0) {
  14. if (((0x01 & pdu[0]) == 0x01)) {
  15. pdu[0] |= 0x04; // TP-RD
  16. pdu[1] = (byte) tracker.mMessageRef; // TP-MR
  17. }
  18. }
  19. if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
  20. //调用RILJ发送短信
  21. mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
  22. IccUtils.bytesToHexString(pdu), reply);
  23. } else {
  24. mCi.sendSMS(IccUtils.bytesToHexString(smsc),
  25. IccUtils.bytesToHexString(pdu), reply);
  26. }
  27. } else {
  28. //通过IMS发送短信
  29. mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
  30. IccUtils.bytesToHexString(pdu), tracker.mImsRetry,
  31. tracker.mMessageRef, reply);
  32. // increment it here, so in case of SMS_FAIL_RETRY over IMS
  33. // next retry will be sent using IMS request again.
  34. tracker.mImsRetry++;
  35. }
  36. }

在上面的发送之前,将SmsTracker中的内容解析出来,通过RILJ发送出去。并且注册了该请求的回应消息EVENT_SEND_SMS_COMPLETE,用于处理短信接收时的流程。

以上就是普通单收件人,短信的发送流程。

整个过程的流程图如下:

下一节来介绍长短信的发送流程

Source: http://blog.csdn.net/u010961631/article/details/50272705

Android短信发送流程之普通短信发送(原)相关推荐

  1. 短信发送流程:系统短信(SMS)发送流程

    1. 点击发送按钮Src/com/android/mms/ui/ComposeMessageActivity.javapublic void onClick(View v) {if ((v == mS ...

  2. Kafka生产者——消息发送流程,同步、异步发送API

    生产者消息发送流程 发送原理 Kafka的Producer发送消息采用的是异步发送的方式. 在消息发送的过程中,涉及到了两个线程:main线程和Sender线程,以及一个线程共享变量:RecordAc ...

  3. Android短信发送流程之长短信发送(原)

    从前面< Android短信发送流程之普通短信发送 >流程看到,长短信与普通短信的流程从SmsManager的sendMultipartTextMessage()方法开始区分,现在我们来看 ...

  4. android+mms发送流程,mms发送流程代码版droid.docx

    Lele was written in 2021 Lele was written in 2021 MMS发送流程代码版droid MMS发送流程(代码版) apps/Mms 1. 点击发送按钮Src ...

  5. Android短信发送流程之多收件人发送(原)

    前面的< Android短信发送流程之长短信发送 >中介绍了长短信对于普通短信的区别,而对于多收件人的情况,在SmsMessageSender的queueMessage()方法中我们了解到 ...

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

    发送前的校验 从短信的点击按钮开始着手: // packages/apps/Mms/src/com/android/mms/ui/ComposeMessageActivity.java@Overrid ...

  7. Android短彩信源码解析-短信发送流程(一)

    转载请注明出处:http://blog.csdn.net/droyon/article/details/10194591 源码版本来自:Android4.0.3 忙了两三个月,终于有时间可以写点东西了 ...

  8. Android监听SMS发送状态并获取短信服务中心号码

    监听SMS发送状态的例子网上虽然有,但还是太杂了不完全.自己写了个. 短信服务中心号码的获取是通过SmsMessage.getServiceCenterAddress()方法获得.也就是只能从已经存储 ...

  9. android 调用短信,android中可以通过两种方式调用接口发送短信

    第一:调用系统短信接口直接发送短信:主要代码如下: //直接调用短信接口发短信 SmsManager smsManager = SmsManager.getDefault(); List divide ...

最新文章

  1. mysql mysqld_multi 单机多进程
  2. mysql 单表多字段查询_单表多字段MySQL模糊查询的实现
  3. iOS下KVO使用过程中的陷阱
  4. java元婴期(23)----java进阶(mybatis(2)---mapper代理mybatis核心配置文件输入输出映射)
  5. Python3算法基础练习:编程100例(6 ~ 10)
  6. 正则表达式总结及一些有用的例子
  7. UI体系的本质是结构化存在
  8. 17-比赛2 C - Maze (dfs)
  9. [CF671E] Organizing a Race
  10. eclipse快捷键备忘
  11. python 100 days github_GitHub - Andyhe2019/Python-100-Days: Python - 100天从新手到大师
  12. 捷联惯导算法与组合导航原理讲义捷联惯导基础知识剖析目录
  13. 我在HW中用到的三款工具
  14. oracle查询分区表分区,查询分区表(查看oracle分区表)
  15. 织梦首页header实现会员的登录及会员状态显示
  16. 撰写SCI论文的选题思路与技巧 - 易智编译EaseEditing
  17. 映美FP-530K+打印发票卡纸
  18. Xcode6 打包报错 ITMS-90096
  19. iOS-QQ分享功能实现
  20. Android中屏蔽有新短信时通知栏里的通知

热门文章

  1. easyui中的datagrid的数据加载的问题
  2. WPF Converter 使用复杂参数的方法
  3. STM32 USART1 USART2 UASRT3 UART4 UART5串口通信测试程序
  4. 你一定要知道的关于Linux文件目录操作的12个常用命令
  5. 【Leetcode】组合、排列、子集、切割(回溯模板和去重方法)
  6. [云炬创业学笔记]第二章决定成为创业者测试16
  7. 云炬随笔20161224
  8. 吴恩达《优化深度神经网络》精炼笔记(1)-- 深度学习的实用层面
  9. python可以开多少线程_Python开启线程,在函数中开线程的实例
  10. D8016“/ZI”和“/Gy-”命令行选项不兼容问题的解决