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(); } } } 分享到: