Android2.2packages/apps/Mms
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 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);
}
ArrayListdeliveryIntents = newArrayList(messageCount);
ArrayListsentIntents = new ArrayList(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();
}
}
}