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

时间:2021-12-08 08:55:12

转载请注明出处:http://blog.csdn.net/droyon/article/details/10194591

源码版本来自:Android4.0.3

忙了两三个月,终于有时间可以写点东西了。

有人问我怎么了解原生应用的流程,可能每个人有最适合每个人的方法,现将我的方法说一下,供大家参考:

1、通过DEBUG,DEBUG可以了解每一步代码做了什么,而且可以看到堆栈执行过程。

2、通过打Log的方式。在自己不了解的流程模块打大量的log信息,根据log信息,摸清“模块”的底细。

上述两种方式都需要足够的耐心,但是逐渐接触谜团的真相的过程,确实很享受。好奇心是程序猿,攻城狮的朋友。

 

相关文章:

---------------------------------------------------------------

2、短信发送framework层逻辑

3、短信发送pdu包压缩解析

---------------------------------------------------------------

现将短信发送的流程整理一下,不当之处请大家斧正。

短信发送大体可以分为下面三个阶段:

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

我们现在详细叙述一下:

1、短信发送上层逻辑流程

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

这个界面叫做“会话信息列表界面”,这个界面保存了短彩信所有的会话条目

(会话:英文名Thread,一条会话会记录当前会话内的短信往来信息,例如图片内的10086会话)

点击左下角的新建按钮,那么我们就会进入下面的这个界面:

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

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

这个界面叫做“信息列表界面”,它记录了会话内短信往来的所有条目。信息条目按照时间倒序排列:最新的信息条目显示在列表最下方。

我们的短信流程就从这个界面开始,当我们输入完要编辑的信息,点击右下角的发送按钮。

类名:ComposeMessageActivity.java

点击事件:

@Override
public void onClick(View v) {
if ((v == mSendButtonSms || v == mSendButtonMms) && isPreparedForSending()) {
confirmSendMessageIfNeeded();
} else if ((v == mRecipientsPicker)) {
launchMultiplePhonePicker();
}
}
方法堆栈进入confirmSendMessageIfNeeded,让我们看看confirmSendMessageIfNeeded()方法:
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)
.setTitle(title)
.setMessage(R.string.invalid_recipient_message)
.setPositiveButton(R.string.try_to_send,
new SendIgnoreInvalidRecipientListener())
.setNegativeButton(R.string.no, new CancelSendingListener())
.show();
} else {
new AlertDialog.Builder(this)
.setTitle(R.string.cannot_send_message)
.setMessage(R.string.cannot_send_message_reason)
.setPositiveButton(R.string.yes, new CancelSendingListener())
.show();
}
} else {
// The recipients editor is still open. Make sure we use what's showing there
// as the destination.
ContactList contacts = mRecipientsEditor.constructContactsFromInput(false);
mDebugRecipients = contacts.serialize();
sendMessage(true);
}
}

首先判断联系人输入框是否可见,如果不可见,那么可以直接发送。

然后检查是否有非法的联系人,如果存在,弹出AlertDialog给用户提示。

如果没有非法的联系人,那么直接发送。方法堆栈进入sendMessage(true)

让我们看看sendMessage(true)的逻辑

private void sendMessage(boolean bCheckEcmMode) {
if (bCheckEcmMode) {
// TODO: expose this in telephony layer for SDK build
String inEcm = SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE);
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) {
if (LogTag.SEVERE_WARNING) {
String sendingRecipients = mConversation.getRecipients().serialize();
if (!sendingRecipients.equals(mDebugRecipients)) {
String workingRecipients = mWorkingMessage.getWorkingRecipients();
if (!mDebugRecipients.equals(workingRecipients)) {
LogTag.warnPossibleRecipientMismatch("ComposeMessageActivity.sendMessage" +
" recipients in window: \"" +
mDebugRecipients + "\" differ from recipients from conv: \"" +
sendingRecipients + "\" and working recipients: " +
workingRecipients, this);
}
}
sanityCheckConversation();
}

// send can change the recipients. Make sure we remove the listeners first and then add
// them back once the recipient list has settled.
removeRecipientsListeners();

mWorkingMessage.send(mDebugRecipients);

mSentMessage = true;
mSendingMessage = true;
addRecipientsListeners();

mScrollOnSend = true; // in the next onQueryComplete, scroll the list to the end.
}
// But bail out if we are supposed to exit after the message is sent.
if (mSendDiscreetMode) {
finish();
}
}
传进来的参数为true,也就是检查是否处于紧急模式,如果处于紧急模式,那么进行相关逻辑的处理,并退出发送。

如果当前非处于紧急模式,那么检查当前是否有正在发送的短信,那么在判断mSendDiscreetMode是否为true,如果为true,那么退出此界面。

如果当前不存在正在发送的短信,那么执行 mWorkingMessage.send(mDebugRecipients)。

让我们进入mWorkingMessage.send()方法:

类名:WorkingMessage.java

public void send(final String recipientsInUI) {
long origThreadId = mConversation.getThreadId();

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
LogTag.debug("send origThreadId: " + origThreadId);
}

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)) {//检查当前信息的mMmsState以及是否含有email地址,来判断当前信息是否会转化为彩信
// uaProfUrl setting in mms_config.xml must be present to send an MMS.
// However, SMS service will still work in the absence of a uaProfUrl address.
if (MmsConfig.getUaProfUrl() == null) {
String err = "WorkingMessage.send MMS sending failure. mms_config.xml is " +
"missing uaProfUrl setting. uaProfUrl is required for MMS service, " +
"but can be absent for SMS.";
RuntimeException ex = new NullPointerException(err);
Log.e(TAG, err, ex);
// now, let's just crash.
throw ex;
}

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

final SlideshowModel slideshow = mSlideshow;
final CharSequence subject = mSubject;
final boolean textOnly = mAttachmentType == TEXT;

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
LogTag.debug("Send mmsUri: " + mmsUri);
}

// Do the dirty work of sending the message off of the main UI thread.
new Thread(new Runnable() {
@Override
public void run() {
final SendReq sendReq = makeSendReq(conv, subject);

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

updateSendStats(conv);
}
}, "WorkingMessage.send MMS").start();
} 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;
}

我们主要看一下短信的逻辑部分:

开启一个线程,主线程返回,在新开启的线程里会执行 preSendSmsWorker()以及updateSendState()这两个方法。让我们看看这两个方法的内容:

preSendSmsWorker:

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

mStatusListener.onPreMessageSent();

long origThreadId = conv.getThreadId();

// Make sure we are still using the correct thread ID for our recipient set.
long threadId = conv.ensureThreadId();

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)))) {
String msg = origThreadId != 0 && origThreadId != threadId ?
"WorkingMessage.preSendSmsWorker threadId changed or " +
"recipients changed. origThreadId: " +
origThreadId + " new threadId: " + threadId +
" also mConversation.getThreadId(): " +
mConversation.getThreadId()
:
"Recipients in window: \"" +
recipientsInUI + "\" differ from recipients from conv: \"" +
semiSepRecipients + "\"";

// Just interrupt the process of sending message if recipient mismatch
LogTag.warnPossibleRecipientMismatch(msg, mActivity);
}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);
}
}

在这个方法中,首先 检查/创建 一个threadId,并且调用sendSmsWorker方法。最后删除当前会话内的草稿短信。

比较重要的是两个方法,第一个是conv.ensureThreadId,另一个是sendSmsWorker,我们先来看一下conv.ensureThreadId这个方法:

Conversation.java//保存了和会话信息有关的信息
public synchronized long ensureThreadId() {        if (DEBUG || DELETEDEBUG) {            LogTag.debug("ensureThreadId before: " + mThreadId);        }        if (mThreadId <= 0) {            mThreadId = getOrCreateThreadId(mContext, mRecipients);        }        if (DEBUG || DELETEDEBUG) {            LogTag.debug("ensureThreadId after: " + mThreadId);        }        return mThreadId;    }

注意这个方法加了同步锁(synchronized),在这个方法中,我们看到,如果mThreadId不小于0,那么返回mThreadId,如果mThreadId小于0,那么,执行getOrCreateThreadId方法。

这个方法有两个参数,第一个参数为Context,重要的是第二个参数,为当前会话的联系人集合(保存了当前要发送的所有联系人)

private static long getOrCreateThreadId(Context context, ContactList list) {
HashSet<String> recipients = new HashSet<String>();
Contact cacheContact = null;
for (Contact c : list) {
cacheContact = Contact.get(c.getNumber(), false);
if (cacheContact != null) {
recipients.add(cacheContact.getNumber());
} else {
recipients.add(c.getNumber());
}
}//将联系人的信息导入到recipients中
synchronized(sDeletingThreadsLock) {//加锁,同步代码快
if (DELETEDEBUG) {
ComposeMessageActivity.log("Conversation getOrCreateThreadId for: " +
list.formatNamesAndNumbers(",") + " sDeletingThreads: " + sDeletingThreads);
}
long now = System.currentTimeMillis();
while (sDeletingThreads) {
try {
sDeletingThreadsLock.wait(30000);
} catch (InterruptedException e) {
}
if (System.currentTimeMillis() - now > 29000) {
// The deleting thread task is stuck or onDeleteComplete wasn't called.
// Unjam ourselves.
Log.e(TAG, "getOrCreateThreadId timed out waiting for delete to complete",
new Exception());
sDeletingThreads = false;
break;
}
}
long retVal = Threads.getOrCreateThreadId(context, recipients);//重要的是这个方法
if (DELETEDEBUG || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
LogTag.debug("[Conversation] getOrCreateThreadId for (%s) returned %d",
recipients, retVal);
}
return retVal;
}
}

在这个方法中,我们通过执行Threads.getOrCreateTheadId方法来得到或者创建一个ThreadId,并返回。

我们进到Threads.getOrCreateTheadId:

Telephony.java
public static long getOrCreateThreadId(                Context context, Set<String> recipients) {            Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon();            for (String recipient : recipients) {                if (Mms.isEmailAddress(recipient)) {                    recipient = Mms.extractAddrSpec(recipient);                }                uriBuilder.appendQueryParameter("recipient", recipient);            }            Uri uri = uriBuilder.build();            //if (DEBUG) Rlog.v(TAG, "getOrCreateThreadId uri: " + uri);            Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),                    uri, ID_PROJECTION, null, null, null);            if (cursor != null) {                try {                    if (cursor.moveToFirst()) {                        return cursor.getLong(0);                    } else {                        Rlog.e(TAG, "getOrCreateThreadId returned no rows!");                    }                } finally {                    cursor.close();                }            }            Rlog.e(TAG, "getOrCreateThreadId failed with uri " + uri.toString());            throw new IllegalArgumentException("Unable to find or allocate a thread ID.");        }    }
我们在这个方法汇总只看到了query,并没有看到insert,那么ThreadId在哪里创建的那?
我们跳转到处理SqliteWrapper.query的地方,根据Uri信息,我们找到了处理的逻辑代码:

MmsSmsProvider.java
query方法:

case URI_THREAD_ID:
List<String> recipients = uri.getQueryParameters("recipient");

cursor = getThreadId(recipients);
break;

我们继续深入,进入到getThreadId方法内:
private synchronized Cursor getThreadId(List<String> recipients) {
Set<Long> addressIds = getAddressIds(recipients);
String recipientIds = "";

if (addressIds.size() == 0) {
Log.e(LOG_TAG, "getThreadId: NO receipients specified -- NOT creating thread",
new Exception());
return null;
} else if (addressIds.size() == 1) {
// optimize for size==1, which should be most of the cases
for (Long addressId : addressIds) {
recipientIds = Long.toString(addressId);
}
} else {
recipientIds = getSpaceSeparatedNumbers(getSortedSet(addressIds));
}

if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.d(LOG_TAG, "getThreadId: recipientIds (selectionArgs) =" +
/*recipientIds*/ "xxxxxxx");
}

String[] selectionArgs = new String[] { recipientIds };

SQLiteDatabase db = mOpenHelper.getReadableDatabase();
db.beginTransaction();
Cursor cursor = null;
try {
// Find the thread with the given recipients
cursor = db.rawQuery(THREAD_QUERY, selectionArgs);

if (cursor.getCount() == 0) {
// No thread with those recipients exists, so create the thread.
cursor.close();

Log.d(LOG_TAG, "getThreadId: create new thread_id for recipients " +
/*recipients*/ "xxxxxxxx");
insertThread(recipientIds, recipients.size());

// The thread was just created, now find it and return it.
cursor = db.rawQuery(THREAD_QUERY, selectionArgs);
}
db.setTransactionSuccessful();
} catch (Throwable ex) {
Log.e(LOG_TAG, ex.getMessage(), ex);
} finally {
db.endTransaction();
}

if (cursor != null && cursor.getCount() > 1) {
Log.w(LOG_TAG, "getThreadId: why is cursorCount=" + cursor.getCount());
}
return cursor;
}

在这个方法内,我相信大家都找到了答案:

首先:

"SELECT _id FROM threads " + "WHERE recipient_ids=?";

如果查询结果数目为0,也就是说当前联系人不存在任何一个会话(Thread)中,那么我们就执行insertThread创建一个会话。

关于insertThread方法:

private void insertThread(String recipientIds, int numberOfRecipients) {
ContentValues values = new ContentValues(4);

long date = System.currentTimeMillis();
values.put(ThreadsColumns.DATE, date - date % 1000);
values.put(ThreadsColumns.RECIPIENT_IDS, recipientIds);
if (numberOfRecipients > 1) {
values.put(Threads.TYPE, Threads.BROADCAST_THREAD);
}
values.put(ThreadsColumns.MESSAGE_COUNT, 0);

long result = mOpenHelper.getWritableDatabase().insert(TABLE_THREADS, null, values);
Log.d(LOG_TAG, "insertThread: created new thread_id " + result +
" for recipientIds " + /*recipientIds*/ "xxxxxxx");

getContext().getContentResolver().notifyChange(MmsSms.CONTENT_URI, null);
}

现在我们得到了ThreadId,我们回到我们之前的地方,继续看看在sendSmsWork方法中的逻辑:

WorkingMessage.java

在之前,我们得到了mThreadId,那么我们就可以继续往下走了

sendSmsWorker(msgText, semiSepRecipients, threadId);

// Be paranoid and clean any draft SMS up.
deleteDraftSmsMessage(threadId);

我们首先来看看,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);
}

mStatusListener.onMessageSent();
MmsWidgetProvider.notifyDatasetChanged(mActivity);
}

new一个SmsMessageSender,将联系人的发送地址,发送内容,会话id(threadId)作为参数,然后调用sendMessage方法。

mStatusListener.onMessageSent()是个回调,告诉ComposeMessageActivity发送完成。

我们主要来看看sendMessage方法。

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

queueMessage:

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

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

检查设置,是否需要发送报告。然后根据mNumberOfDests,执行Sms.addMessageToUri,将信息插入到数据库中。

我们看看插入过程:

Telephony.java

public static Uri addMessageToUri(ContentResolver resolver,
Uri uri, String address, String body, String subject,
Long date, boolean read, boolean deliveryReport, long threadId) {
ContentValues values = new ContentValues(7);

values.put(ADDRESS, address);
if (date != null) {
values.put(DATE, date);
}
values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0));
values.put(SUBJECT, subject);
values.put(BODY, body);
if (deliveryReport) {
values.put(STATUS, STATUS_PENDING);
}
if (threadId != -1L) {
values.put(THREAD_ID, threadId);
}
return resolver.insert(uri, values);
}

根据Uri,找到相应的ContentProvider,然后信息会被插入数据库 sms表,信息包括,短信内容,发送时间,信息类型(type = Sms.MESSAGE_TYPE_QUEUED;),等。

sms表上有若干触发器,此处会触发这个触发器:

CREATE TRIGGER sms_update_thread_on_insert AFTER INSERT ON sms BEGIN  UPDATE threads SET    date = (strftime('%s','now') * 1000),     snippet = new.body,     snippet_cs = 0  WHERE threads._id = new.thread_id;   UPDATE threads SET message_count =      (SELECT COUNT(sms._id) FROM sms LEFT JOIN threads       ON threads._id = thread_id      WHERE thread_id = new.thread_id        AND sms.type != 3) +      (SELECT COUNT(pdu._id) FROM pdu LEFT JOIN threads       ON threads._id = thread_id      WHERE thread_id = new.thread_id        AND (m_type=132 OR m_type=130 OR m_type=128)        AND msg_box != 3)   WHERE threads._id = new.thread_id;   UPDATE threads SET read =     CASE (SELECT COUNT(*)          FROM sms          WHERE read = 0            AND thread_id = threads._id)      WHEN 0 THEN 1      ELSE 0    END  WHERE threads._id = new.thread_id; END

意思是在向sms数据表内插入数据后,会更新thread数据表。大家感兴趣的可以自己看一下,大体更新thread表的message_count等。

回到刚才那里,将信息插入到数据库之后,就发起一个广播,然后整个方法堆栈就结束了。

广播发送代码:

mContext.sendBroadcast(new Intent(SmsReceiverService.ACTION_SEND_MESSAGE,
null,
mContext,
SmsReceiver.class));

注意此处的action:
SmsReceiverService.ACTION_SEND_MESSAGE


我们看看广播中的处理逻辑:

SmsReceiver.java

@Override
public void onReceive(Context context, Intent intent) {
onReceiveWithPrivilege(context, intent, false);
}
onReceiveWithPrivilege:

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_RECEIVED_ACTION)) {//接受信息的action
return;
}

intent.setClass(context, SmsReceiverService.class);
intent.putExtra("result", getResultCode());
beginStartingService(context, intent);//开启service
}


beginStartingService:


public static void beginStartingService(Context context, Intent intent) {
synchronized (mStartingServiceSync) {
if (mStartingService == null) {
PowerManager pm =
(PowerManager)context.getSystemService(Context.POWER_SERVICE);
mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"StartingAlertService");
mStartingService.setReferenceCounted(false);
}
mStartingService.acquire();
context.startService(intent);//开启service
}
}

综合来说,这段代码的作用就是在广播中开启了一个service。

我们看看service中的逻辑

smsReceiverService:

public void onCreate() {
// Temporarily removed for this duplicate message track down.
// if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE) || LogTag.DEBUG_SEND) {
// Log.v(TAG, "onCreate");
// }

// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block.
HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
thread.start();

mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

首先开启一个新的线程,线程的looper用于处理ServiceHandler中的作业。

public int onStartCommand(Intent intent, int flags, int startId) {
// Temporarily removed for this duplicate message track down.

mResultCode = intent != null ? intent.getIntExtra("result", 0) : 0;

if (mResultCode != 0) {
Log.v(TAG, "onStart: #" + startId + " mResultCode: " + mResultCode +
" = " + translateResultCode(mResultCode));
}

Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
return Service.START_NOT_STICKY;
}

将从广播过来的Intent信息包装秤Message作业发送到ServiceHandler中处理:
public void handleMessage(Message msg) {
int serviceId = msg.arg1;
Intent intent = (Intent)msg.obj;
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "handleMessage serviceId: " + serviceId + " intent: " + intent);
}
if (intent != null) {
String action = intent.getAction();

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

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "handleMessage action: " + action + " error: " + error);
}

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();
} else if (ACTION_SEND_INACTIVE_MESSAGE.equals(action)) {
handleSendInactiveMessage();
}
}
// 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);
}

我们的action:SmsReceiverService.ACTION_SEND_MESSAGE,那么接下来执行:

handleSendMessage()

private void handleSendMessage() {
if (!mSending) {
sendFirstQueuedMessage();
}
}

sendFirstQueueMessage:

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

我们刚才将信息插入到sms数据表中,type = Sms.MESSAGE_TYPE_QUEUED,那么此处将这些信息加载上来,遍历发送。

在发送过程中,new一个SmsSingleRecipientSender,将address,msgTest,threadId,是否需要发送报告   ,等作为参数。然后执行此对象的sendMessage方法。

在sendMessage方法中:

public SmsSingleRecipientSender(Context context, String dest, String msgText, long threadId,
boolean requestDeliveryReport, Uri uri) {
super(context, null, msgText, threadId);
mRequestDeliveryReport = requestDeliveryReport;
mDest = dest;
mUri = uri;
}

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();//得到SmsMessager对象
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 {
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 + "\"");
}

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<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(messageCount);//构建两个peddingIntent,用于在信息发送完成之后,回调发送广播。或者在接收到短信状态报告之后回调发送广播
ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(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;
}

总结来说上面的方法做了五件事:

1、得到SmsMessager对象。

2、将信息内容按照GSM/CDMA等制式分段,也就是看需要几条信息将上述信息内容发送出去。因为每条信息有字数限制

3、更新短信类别,更新到发件箱

4、构建两个Peddingintent,这两个Penddingintent用于在信息发送完成之后,或者在接收到短信状态报告之后回调发送广播,这个广播也会在这个service中处理。

5、调用SmsManager发送信息。


以上就是短信发送的上层逻辑,从smsManager.sendMultipartTextMessage之后就要进入到framework层。

我们今天就先介绍短信发送流程的上层逻辑流程,关于后面的两部分,请关注更新。