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

1.      点击发送按钮Src/com/android/mms/ui/ComposeMessageActivity.java

public void onClick(View v) {

        if ((v == mSendButton) && isPreparedForSending()) {

            confirmSendMessageIfNeeded(); //确认是否需要发送短信—-》

        }

}

 

2.src/com/android/mms/ui/ComposeMessageActivity.java

private void confirmSendMessageIfNeeded() {

        if (!isRecipientsEditorVisible()) {  //编辑联系人不可见时,也就是给已存在会话的联系人发送短信时

            sendMessage(true);

            return;

        }

 

        boolean isMms = mWorkingMessage.requiresMms();   //是否需要以彩信形式发送

                if (mRecipientsEditor.hasInvalidRecipient(isMms)) {//是否含有不合法的收件人

            if (mRecipientsEditor.hasValidRecipient(isMms)) {//有合法的和不合法的,弹出尝试发送对话框

                String title =getResourcesString(R.string.has_invalid_recipient,

                        mRecipientsEditor.formatInvalidNumbers(isMms));

                new AlertDialog.Builder(this)

                   .setIcon(android.R.drawable.ic_dialog_alert)

                    .setTitle(title)

                    .setMessage(R.string.invalid_recipient_message)

                   .setPositiveButton(R.string.try_to_send,

                            newSendIgnoreInvalidRecipientListener())

                   .setNegativeButton(R.string.no, new CancelSendingListener())

                    .show();

            } else {//如果全是不合法的联系人,提示不能发送信息

                new AlertDialog.Builder(this)

                   .setIcon(android.R.drawable.ic_dialog_alert)

                    .setTitle(R.string.cannot_send_message)

                    .setMessage(R.string.cannot_send_message_reason)

                   .setPositiveButton(R.string.yes, new CancelSendingListener())

                    .show();

            }

        } else {//判断收件人没有问题,接着发送信息 --》

            sendMessage(true);

        }

}

 

3. src/com/android/mms/ui/ComposeMessageActivity.java

private void sendMessage(boolean bCheckEcmMode) {

    Log.v(TAG, "sendMessage");

        if (bCheckEcmMode) {

            // TODO: expose this in telephony layer for SDK build

            String inEcm = SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE);     //判断电话是否处于紧急拨号模式,得到的inEcm一般为空

            Log.v(TAG, "inEcm = " + inEcm);

            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 message

                    Log.e(TAG, "Cannot find EmergencyCallbackModeExitDialog", e);

                }

            }

        }

 

        if (!mSendingMessage) {

            // send can change the recipients. Make sure we remove the listeners firstand then add

            // them back once the recipient list has settled.

            removeRecipientsListeners();  //取消对收件人的监听

            mWorkingMessage.send();   //发送信息—-》

            mSentMessage = true;

            mSendingMessage = true;

            addRecipientsListeners(); //重新添加收件人监听

        }

        // But bail out if we are supposed to exit after the message is sent.

        if (mExitOnSent) {//如果mExitOnSent为true,信息发送完成后退出Activity

            finish();

        }

    }

 

4. src/com/android/mms/data/WorkingMessage.java

/**

     * Send this message over the network.  Will call back with onMessageSent() once

     * it has been dispatched to the telephonystack.  This WorkingMessage object is

     * no longer useful after this method hasbeen called.

     */

    public void send() {

        if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {

            LogTag.debug("send");

        }

 

        // Get ready to write to disk.

        prepareForSave(true /* notify */);//主要做一下同步收件人和WorkingMessage,彩信时在准备其他一些东西

 

        // We need the recipient list for both SMS and MMS.

        final Conversation conv = mConversation;

        String msgTxt = mText.toString();

        Log.v(TAG, "msgText = " + msgTxt);

        if (requiresMms() ||addressContainsEmailToMms(conv, msgTxt)) {

            // 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(mContext);

 

            final SlideshowModel slideshow = mSlideshow;

            final SendReq sendReq = makeSendReq(conv,mSubject);

 

            // Do the dirty work of sending the message off of the main UI thread.

            new Thread(new Runnable() {

                public void run() {

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

                }

            }).start();

        } else {

            // Same rules apply as above.

            final String msgText = mText.toString();//取出短消息

            Log.v(TAG, "msgText = " + msgText);

            new Thread(new Runnable() {

                public void run() {

                    preSendSmsWorker(conv, msgText);//发送信息--》

                }

            }).start();

        }

 

        // update the Recipient cache with the new to address, if it's different

        RecipientIdCache.updateNumbers(conv.getThreadId(),conv.getRecipients());

 

        // Mark the message as discarded because it is "off the market"after being sent.

        mDiscarded = true;

    }

 

5. src/com/android/mms/data/WorkingMessage.java

private void preSendSmsWorker(Conversation conv, StringmsgText) {

        // If user tries to send the message, it's a signal the inputtedtext is what they wanted.

        UserHappinessSignals.userAcceptedImeText(mContext);

 

        mStatusListener.onPreMessageSent();//重置一些信息,比如清空输入内容框、一些监听等等

 

        // Make sure we are still using the correct thread ID for our

        // recipient set.

        long threadId = conv.ensureThreadId();//新建获得会话线程ID

        Log.v(TAG, "threadId = " + threadId);

        final String semiSepRecipients =conv.getRecipients().serialize();

 

        // just do a regular send. We're already on a non-ui thread so noneed to fire

        // off another thread to do this work.

        sendSmsWorker(msgText, semiSepRecipients, threadId);//发送信息----》

 

        // Be paranoid and clean any draft SMS up.

        deleteDraftSmsMessage(threadId);//删除草稿

    }

 

6. src/com/android/mms/data/WorkingMessage.java

private void sendSmsWorker(String msgText, String semiSepRecipients, longthreaded) {

        String[] dests = TextUtils.split(semiSepRecipients,“;”);

        Log.v(TAG, “sendSmsWorker – semiSepRecipients is “ + semiSepRecipients);

        if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {

            LogTag.debug(“sendSmsWorker sending message”);

        }

        MessageSender sender = new SmsMessageSender(mContext, dests, msgText, threaded);

        try {

            sender.sendMessage(threadId);//根据ThreadID发送信息----》

 

            // Make sure this thread isn't over the limits in message count

            Recycler.getSmsRecycler().deleteOldMessagesByThreadId(mContext, threadId);

        } catch (Exception e) {

            Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e);

        }

 

        mStatusListener.onMessageSent();

    }

7. src/com/android/mms/transaction/SmsMessageSender.java

public boolean sendMessage(long token) throwsMmsException {

        // In order to send the message one by one, instead of sending now, themessage will split,

        // and be put into the queue along with each destinations

        return queueMessage(token);

    }

8. src/com/android/mms/transaction/SmsMessageSender.java

private boolean queueMessage(long token) throwsMmsException {

        if ((mMessageText == null) || (mNumberOfDests == 0)) {

            // Don't try to send an empty message.

            throw new MmsException("Null message body or dest.");

        }

        Log.v("SMsMessageSender", "queueMessage");

        SharedPreferences prefs =PreferenceManager.getDefaultSharedPreferences(mContext);

        boolean requestDeliveryReport =prefs.getBoolean(

                MessagingPreferenceActivity.SMS_DELIVERY_REPORT_MODE,

                DEFAULT_DELIVERY_REPORT_MODE);

        Log.v("SmsMessageSender", "add Message to 'content://sms/queued'");

        for (int i = 0; i < mNumberOfDests; i++) {//根据收件人数目分别建立短信放入发送队列

            try {

                Sms.addMessageToUri(mContext.getContentResolver(),

                        Uri.parse("content://sms/queued"), mDests[i],

                        mMessageText, null, mTimestamp,

                        true /* read */,

                        requestDeliveryReport,

                        mThreadId);

            } catch (SQLiteException e) {

                SqliteWrapper.checkSQLiteException(mContext, e);

            }

        }

        // Notify the SmsReceiverService to send the message out

        mContext.sendBroadcast(new Intent(SmsReceiverService.ACTION_SEND_MESSAGE,

                null,

                mContext,

                SmsReceiver.class));  //通知SmsReceiverService来发送短信,传递参数ACTION_SEND_MESSAGE

        return false;

    }

 

9. src/com/android/mms/transaction/SmsReceiverService.java

/**

         * Handle incoming transactionrequests.

         * The incoming requests are initiatedby the MMSC Server or by the MMS Client itself.

         */

        @Override

        public void handleMessage(Message msg) {

            int serviceId = msg.arg1;

            Intent intent = (Intent)msg.obj;

            if (intent != null) {

                String action =intent.getAction();

 

                int error = intent.getIntExtra("errorCode", 0);

 

                if (MESSAGE_SENT_ACTION.equals(intent.getAction())){

                    handleSmsSent(intent,error);

                } else if (SMS_RECEIVED_ACTION.equals(action)) {

                    handleSmsReceived(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();//处理发送信息

                }

            }

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

        }

    }

10. src/com/android/mms/transaction/SmsReceiverService.java

private void handleSendMessage(){

    Log.v(TAG, "handleSendMessage");

        if (!mSending) {//如果没有发送,则准备发送

            sendFirstQueuedMessage();

        }

}

 

11. src/com/android/mms/transaction/SmsReceiverService.java

public synchronized void sendFirstQueuedMessage() {

    Log.v(TAG, "sendFirstQueuedMessage");

        boolean success = true;

        // get all the queued messages from the database

        final Uri uri = Uri.parse("content://sms/queued");

        ContentResolver resolver =getContentResolver();

//查询队列中的信息,包括上次没有发送出去存放在发送队列的信息       

Cursor c = SqliteWrapper.query(this, resolver, uri,

                        SEND_PROJECTION, null, null, "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);

                    Log.v(TAG, "address = " + address);

                    Log.v(TAG, "msgText = " + msgText);

                    Log.v(TAG, "status = " + status);

                   

                    int msgId = c.getInt(SEND_COLUMN_ID);

                    Uri msgUri = ContentUris.withAppendedId(Sms.CONTENT_URI,msgId);

                    Log.v(TAG, "msgId = " + msgId);

                    SmsMessageSender sender = newSmsSingleRecipientSender(this,

                            address, msgText,threadId, status == Sms.STATUS_PENDING,

                            msgUri);

 

                    if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {

                        Log.v(TAG, "sendFirstQueuedMessage " + msgUri +

                                ", address: " + address +

                                ", threadId: " + threadId +

                                ", body: " + msgText);

                    }

                    try {

                        sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);//进行单个信息发送

                        mSending = true;

                    } catch (MmsExceptione) {

                        Log.e(TAG, "sendFirstQueuedMessage: failed to send message" + msgUri

                                + ", caught ", e);

                        success = false;

                    }

                }

            } 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.

            unRegisterForServiceStateChanges();

        }

}

 

12. src/com/android/mms/transaction/SmsSingleRecipientSender.java

public boolean sendMessage(long token) throwsMmsException {

        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.getDefault();

        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 {

        Log.v("SmsSingleRecipient", "divideMessage");

           messages = smsManager.divideMessage(mMessageText);//短信通道被限制160个字节,因此内容过长将会以多条短信发送,这个动作就是将长短信拆分成合适的大小

           // remove spaces from destination number (e.g. "801 555 1212"-> "8015551212")

           mDest = mDest.replaceAll("", "");

        }

        int messageCount = messages.size();

 

        if (messageCount ==0) {

            // Don't try to send an empty message.

            throw new MmsException("SmsMessageSender.sendMessage: divideMessage returned " +

                    "empty messages. Original message is \"" + mMessageText + "\"");

        }

        Log.v("SmsSingleRecipientSender", "move to Sms.MESSAGE_TYPE_OUTBOX");

        Log.v("SmsSingleRecipientSender", "mUri = " + mUri);

        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 =  newArrayList<PendingIntent>(messageCount);

        ArrayList<PendingIntent>sentIntents = new ArrayList<PendingIntent>(messageCount);

        for (int i = 0; i < messageCount; i++) {

            if (mRequestDeliveryReport) {

                // TODO: Fix: It should not be necessary to

                // specifythe 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));

            }

            Intent intent  = new Intent(SmsReceiverService.MESSAGE_SENT_ACTION,

                    mUri,

                    mContext,

                    SmsReceiver.class);//触发SmsReceiverService的MESSAGE_SENT_ACTION消息

            if (i == messageCount -1) {

               intent.putExtra(SmsReceiverService.EXTRA_MESSAGE_SENT_SEND_NEXT, true);

            }

            sentIntents.add(PendingIntent.getBroadcast(

                    mContext, 0, intent, 0));//设置回调的Intent为intent,

        }

        try {

            smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents);//调用Framework中的API来发送短信,会回调sentIntents来处理发送的情况

        } catch (Exception ex) {

            throw new MmsException("SmsMessageSender.sendMessage: caught " + ex +

                    " from SmsManager.sendTextMessage()");

        }

        if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {

            log("sendMessage: address=" + mDest + ", threadId=" + mThreadId +

                    ", uri=" + mUri + ", msgs.count=" + messageCount);

        }

        return false;

    }

 

13. src/com/android/mms/transaction/SmsReceiverService.java

/**

         * Handle incoming transactionrequests.

         * The incoming requests are initiatedby the MMSC Server or by the MMS Client itself.

         */

        @Override

        public void handleMessage(Message msg) {

            int serviceId = msg.arg1;

            Intent intent = (Intent)msg.obj;

            if (intent != null) {

                String action =intent.getAction();

 

                int error = intent.getIntExtra("errorCode", 0);

 

                if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {

                    handleSmsSent(intent, error);

                } else if (SMS_RECEIVED_ACTION.equals(action)) {

                    handleSmsReceived(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();

                }

            }

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

        }

    }

 

14. src/com/android/mms/transaction/SmsReceiverService.java

private void handleSmsSent(Intentintent, int error) {

    Log.v(TAG, "handleSmsSent - error is " + error);

        Uri uri = intent.getData();

        Log.v(TAG, "uri = " + uri);

        mSending = false;

        boolean sendNextMsg =intent.getBooleanExtra(EXTRA_MESSAGE_SENT_SEND_NEXT, false);

 

        if (mResultCode == Activity.RESULT_OK) {//发送成功的情况

        Log.v(TAG, "mResultCode == Activity.RESULT_OK");

            if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {

                Log.v(TAG, "handleSmsSent sending uri: " + uri);

            }

            Log.v(TAG, "moveMessageToFolder intoSms.MESSAGE_TYPE_SENT");

//将短信移动到已发送

            if (!Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_SENT,error)) {

                Log.e(TAG, "handleSmsSent: failed to move message " + uri + " to sentfolder");

            }

            if (sendNextMsg) {//如果需要,发送下一条

                sendFirstQueuedMessage();//只是查发送队列的信息去发送

            }

 

            // Update the notification for failed messages since they may be deleted.

            MessagingNotification.updateSendFailedNotification(this);

        } else if ((mResultCode == SmsManager.RESULT_ERROR_RADIO_OFF) ||

                (mResultCode == SmsManager.RESULT_ERROR_NO_SERVICE)) {

        Log.v(TAG, "mResultCode ==SmsManager.RESULT_ERROR_RADIO_OFF");

            if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {

                Log.v(TAG, "handleSmsSent: no service, queuing message w/ uri:" + uri);

            }

            // We got an error with no service or no radio. Register for state changesso

            // when the status of the connection/radio changes, we can try to send the

            // queued up messages.

            registerForServiceStateChanges();//来服务和信号时调用sendFirstQueuedMessage()去发送

            // We couldn't send the message, put in the queue to retry later.

            Log.v(TAG, "uri = " + uri);

            Log.v(TAG, "moveMessageToFolder intoSms.MESSAGE_TYPE_QUEUED");

            Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_QUEUED,error);

            mToastHandler.post(new Runnable() {

                public void run() {

                    Toast.makeText(SmsReceiverService.this,getString(R.string.message_queued),

                            Toast.LENGTH_SHORT).show();

                }

            });

        } else {

        Log.v(TAG, "mResultCode == Other exception");

            if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {

                Log.v(TAG, "handleSmsSent msg failed uri: " + uri);

            }

            Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_FAILED,error);

            MessagingNotification.notifySendFailed(getApplicationContext(),true);

            if (sendNextMsg) {

                sendFirstQueuedMessage();

            }

        }

    }

分享到: 

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页