1.send
packages\apps\Mms\src\com\android\mms\data\WorkingMessage.java
public void send(final String recipientsInUI) {
long origThreadId = mConversation.getThreadId();
removeSubjectIfEmpty(true /* notify */);
// Get ready to write to disk.
prepareForSave(true /* notify */);
// We need the recipient list for both SMS and MMS.
final Conversation conv = mConversation;
String msgTxt = mText.toString();
if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {
//彩信
} else {
// Same rules apply as above.
final String msgText = mText.toString();
new Thread(new Runnable() {
@Override
public void run() {
preSendSmsWorker(conv, msgText, recipientsInUI);
updateSendStats(conv);
}
}, "WorkingMessage.send SMS").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;
}
2. preSendSmsWorker
packages\apps\Mms\src\com\android\mms\data\WorkingMessage.java
private void preSendSmsWorker(Conversation conv, String msgText, String recipientsInUI) {
// If user tries to send the message, it's a signal the inputted text is what they wanted.
UserHappinessSignals.userAcceptedImeText(mActivity);
//把内容编辑框,联系人输入框,mWorkingMessage等恢复原样
mStatusListener.onPreMessageSent();
long origThreadId = conv.getThreadId();
// Make sure we are still using the correct thread ID for our recipient set.
long threadId = conv.ensureThreadId(); //最终通过Threads.getOrCreateThreadId创建/查询threadId
String semiSepRecipients = conv.getRecipients().serialize();
// recipientsInUI can be empty when the user types in a number and hits send
if (LogTag.SEVERE_WARNING && ((origThreadId != 0 && origThreadId != threadId) ||
(!semiSepRecipients.equals(recipientsInUI) && !TextUtils.isEmpty(recipientsInUI)))) {
}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.
sendSmsWorker(msgText, semiSepRecipients, threadId);
// Be paranoid and clean any draft SMS up.
deleteDraftSmsMessage(threadId); //删除数据库的草稿
}
}
3.sendSmsWorker
private void sendSmsWorker(String msgText, String semiSepRecipients, long threadId) {
String[] dests = TextUtils.split(semiSepRecipients, ";");
if (LogTag.VERBOSE || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.d(LogTag.TRANSACTION, "sendSmsWorker sending message: recipients=" +
semiSepRecipients + ", threadId=" + threadId);
}
MessageSender sender = new SmsMessageSender(mActivity, dests, msgText, threadId);
try {
sender.sendMessage(threadId);
//如果一条会话的信息条数超过限制,会删除掉该会话的时间靠前的信息(数据库通过时间过滤)
// Make sure this thread isn't over the limits in message count
Recycler.getSmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId);
} catch (Exception e) {
Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e);
}
//1.重新查询信息列表
mStatusListener.onMessageSent();
MmsWidgetProvider.notifyDatasetChanged(mActivity);
}
4.sendMessage
packages\apps\Mms\src\com\android\mms\transaction\SmsMessageSender.java
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 destinations
return queueMessage(token);
}
5.queueMessage
packages\apps\Mms\src\com\android\mms\transaction\SmsMessageSender.java
private boolean queueMessage(long token) throws MmsException {
if ((mMessageText == null) || (mNumberOfDests == 0)) {
// Don't try to send an empty message.
throw new MmsException("Null message body or dest.");
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
boolean requestDeliveryReport = prefs.getBoolean(
MessagingPreferenceActivity.SMS_DELIVERY_REPORT_MODE,
DEFAULT_DELIVERY_REPORT_MODE);
//几个收件人,就会存入几条信息(MESSAGE_TYPE_QUEUED = 6)
for (int i = 0; i < mNumberOfDests; i++) {
try {
if (LogTag.DEBUG_SEND) {
Log.v(TAG, "queueMessage mDests[i]: " + mDests[i] + " mThreadId: " + mThreadId);
}
Sms.addMessageToUri(mContext.getContentResolver(),
Uri.parse("content://sms/queued"), mDests[i],
mMessageText, null, mTimestamp,
true /* read */,
requestDeliveryReport,
mThreadId);
} catch (SQLiteException e) {
if (LogTag.DEBUG_SEND) {
Log.e(TAG, "queueMessage 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));
return false;
}
6.onReceive
packages\apps\Mms\src\com\android\mms\transaction\SmsReceiver.java
public void onReceive(Context context, Intent intent) {
onReceiveWithPrivilege(context, intent, false);
}
7.onReceiveWithPrivilege
packages\apps\Mms\src\com\android\mms\transaction\SmsReceiver.java
protected void onReceiveWithPrivilege(Context context, Intent intent, boolean 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 && intent.getAction().equals(Intents.SMS_DELIVER_ACTION)) {
return;
}
intent.setClass(context, SmsReceiverService.class);
intent.putExtra("result", getResultCode());
beginStartingService(context, intent);
}
8.beginStartingService
packages\apps\Mms\src\com\android\mms\transaction\SmsReceiver.java
public static void beginStartingService(Context context, Intent intent) {
synchronized (mStartingServiceSync) {
if (mStartingService == null) {
PowerManager pm =
(PowerManager)context.getSystemService(Context.POWER_SERVICE);
//PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。
mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"StartingAlertService");
mStartingService.setReferenceCounted(false);
}
mStartingService.acquire();
context.startService(intent);
}
}
9.handleSendMessage
packages\apps\Mms\src\com\android\mms\transaction\SmsReceiverService.java
private void handleSendMessage() {
if (!mSending) {
sendFirstQueuedMessage();
}
}
10.sendFirstQueuedMessage
packages\apps\Mms\src\com\android\mms\transaction\SmsReceiverService.java
public synchronized void 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);
int msgId = c.getInt(SEND_COLUMN_ID);
Uri msgUri = ContentUris.withAppendedId(Sms.CONTENT_URI, msgId);
SmsMessageSender sender = new SmsSingleRecipientSender(this,
address, msgText, threadId, status == Sms.STATUS_PENDING,
msgUri);
if (LogTag.DEBUG_SEND ||
LogTag.VERBOSE ||
Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "sendFirstQueuedMessage " + msgUri +
", address: " + address +
", threadId: " + threadId);
}
try {
sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);;
mSending = true;
} catch (MmsException e) {
Log.e(TAG, "sendFirstQueuedMessage: failed to send message " + msgUri
+ ", caught ", e);
mSending = false;
//发生异常:将短信type更新为MESSAGE_TYPE_FAILED(5),这里的error为1
messageFailedToSend(msgUri, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
success = false;
// Sending current message fails. Try to send more pending messages
// if there is any.
//发送下一条需要发送的短信
sendBroadcast(new Intent(SmsReceiverService.ACTION_SEND_MESSAGE,
null,
this,
SmsReceiver.class));
}
}
} 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();
}
}
11.sendMessage
packages\apps\Mms\src\com\android\mms\transaction\SmsSingleRecipientSender.java
public boolean sendMessage(long token) throws MmsException {
if (LogTag.DEBUG_SEND) {
Log.v(TAG, "sendMessage token: " + token);
}
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 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);
}
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 + "\"");
}
//更新信息type为MESSAGE_TYPE_OUTBOX(4)
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);
}
if (LogTag.DEBUG_SEND) {
Log.v(TAG, "sendMessage mDest: " + mDest + " mRequestDeliveryReport: " +
mRequestDeliveryReport);
}
ArrayList deliveryIntents = new ArrayList(messageCount);
ArrayList sentIntents = new ArrayList(messageCount);
for (int i = 0; i < messageCount; i++) {
if (mRequestDeliveryReport && (i == (messageCount - 1))) {
// 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 = new Intent(SmsReceiverService.MESSAGE_SENT_ACTION,
mUri,
mContext,
SmsReceiver.class);
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);
}
if (LogTag.DEBUG_SEND) {
Log.v(TAG, "sendMessage sendIntent: " + intent);
}
sentIntents.add(PendingIntent.getBroadcast(mContext, requestCode, intent, 0));
}
try {
smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents);
} catch (Exception ex) {
Log.e(TAG, "SmsMessageSender.sendMessage: caught", ex);
throw new MmsException("SmsMessageSender.sendMessage: caught " + ex +
" from SmsManager.sendTextMessage()");
}
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE) || LogTag.DEBUG_SEND) {
log("sendMessage: address=" + mDest + ", threadId=" + mThreadId +
", uri=" + mUri + ", msgs.count=" + messageCount);
}
return false;
}
12.sendMultipartTextMessage
frameworks\base\telephony\java\android\telephony\SmsManager.java
public void sendMultipartTextMessage(
String destinationAddress, String scAddress, ArrayList parts,
ArrayList sentIntents, ArrayList deliveryIntents) {
sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
deliveryIntents, true /* persistMessage*/);
}
13.sendMultipartTextMessageInternal
frameworks\base\telephony\java\android\telephony\SmsManager.java
private void sendMultipartTextMessageInternal(
String destinationAddress, String scAddress, List parts,
List sentIntents, List deliveryIntents,
boolean persistMessage) {
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();
iccISms.sendMultipartTextForSubscriber(getSubscriptionId(),
ActivityThread.currentPackageName(),
destinationAddress, scAddress, parts,
sentIntents, deliveryIntents, persistMessage);
} 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);
}
}