点击新建短信之后
@Override
protected void onCreate(Bundle savedInstanceState) {
mIsSmsEnabled = MmsConfig.isSmsEnabled(this);
super.onCreate(savedInstanceState);
resetConfiguration(getResources().getConfiguration());
if (BrcmDualSimUtils.isSupportDualSim()) {
setContentView(R.layout.compose_message_activity_brcm);
} else {
setContentView(R.layout.compose_message_activity);
}
setProgressBarVisibility(false);
// Initialize members for UI elements.
initResourceRefs();
mContentResolver = getContentResolver();
mBackgroundQueryHandler = new BackgroundQueryHandler(mContentResolver);
initialize(savedInstanceState, 0);
if (TRACE) {
android.os.Debug.startMethodTracing("compose");
}
updateDualSimFunctionMonitor(true);
}
首先是判断短信功能是否可用,接着根据是否支持双卡,显示不同的编辑界面。接着隐藏带进度的进度条(setProgressBarVisibility(false);),然后初始化界面元素(initResourceRefs();)。
这个界面叫做“会话信息列表界面”,这个界面保存了短彩信所有的会话条目
(会话:英文名Thread,一条会话会记录当前会话内的短信往来信息,例如图片内的10086会话)
点击左下角的新建按钮,那么我们就会进入下面的这个界面:
这个界面叫做“信息列表界面”,它记录了会话内短信往来的所有条目。信息条目按照时间倒序排列:最新的信息条目显示在列表最下方。
(int simId)我们的短信流程就从这个界面开始,当我们输入完要编辑的信息,点击右下角的发送按钮。
类名:ComposeMessageActivity.java
//==========================================================
// Interface methods
//==========================================================
@Override
public void onClick(View v) {
if ((v == mSendButtonSms || v == mSendButtonMms) && isPreparedForSending()) {
if ( BrcmDualSimUtils.isSupportDualSim() ) {
showDialog(SIM_CARD_CHOOSER_ID, null);
handleSendButtonDisplay();
} else {
confirmSendMessageIfNeeded();
}
} else if ((v == mRecipientsPicker)) {
launchMultiplePhonePicker();
}
else if (BrcmDualSimUtils.isSupportDualSim() && (v == mSendButtonSim1) && isPreparedForSending()) {
confirmSendMessageIfNeeded(SimCardID.ID_ZERO.toInt());
}
else if (BrcmDualSimUtils.isSupportDualSim() && (v == mSendButtonSim2) && isPreparedForSending()) {
confirmSendMessageIfNeeded(SimCardID.ID_ONE.toInt());
}
}
方法堆栈进入confirmSendMessageIfNeeded,让我们看看confirmSendMessageIfNeeded方法:
private void confirmSendMessageIfNeeded(int simId) {
if (!isRecipientsEditorVisible()) {
sendMessage(true, simId);
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, simId);
}
}
首先判断联系人输入框是否可见,如果不可见,那么可以直接发送。
然后检查是否有非法的联系人,如果存在,弹出AlertDialog给用户提示。
如果没有非法的联系人,那么直接发送。方法堆栈进入sendMessage(true,simId)
让我们看看sendMessage(true,simId)的逻辑
private void sendMessage(boolean bCheckEcmMode,int simId) {
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(mConversation.isDeletingThreads())
{
Toast.makeText(this, R.string.deleting_conversations_nosend, Toast.LENGTH_LONG).show();
return;
}
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, simId);
mSentMessage = true;
mSendingMessage = true;
addRecipientsListeners();
//android.util.Log.e("composemessageactivity", "Remove me later====>sendMessage() sim id = "+simId);
mScrollOnSend = true; // in the next onQueryComplete, scroll the list to the end.
}
}
传进来的参数为true,也就是检查是否处于紧急模式,如果处于紧急模式,那么进行相关逻辑的处理,并退出发送。
如果当前非处于紧急模式,那么检查当前是否有正在删除的短信,如果有(true),则弹出Toast并退出发送。
如果没有正在删除的信息,则判断有没有发送中的短信,如果有那么退出此界面。
如果当前不存在正在发送的短信,那么执行 mWorkingMessage.send(mDebugRecipients , simId)。
让我们进入mWorkingMessage.send(mDebugRecipients , simId)方法:
public void send(final String recipientsInUI, int simId) {
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();
final int id = simId;
if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {
// 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, id);
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, id);
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;
}
首先检查当前信息的mMmsState以及是否含有email地址,来判断当前信息是否会转化为彩信 ,我发送的是短信,不需要转换,跳到处理处理短信的逻辑 :
开启一个线程,主线程返回,在新开启的线程里会执行 preSendSmsWorker()以及updateSendState()这两个方法。
让我们看看这两个方法的内容:
preSendSmsWorker:
private void preSendSmsWorker(Conversation conv, String msgText, String recipientsInUI, int simId) {
// 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(simId);
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 + "\"";
LogTag.warnPossibleRecipientMismatch(msg, mActivity);
}
// 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, simId);
// Be paranoid and clean any draft SMS up.
deleteDraftSmsMessage(threadId);
}
在这个方法中,首先 检查/创建 一个threadId,并且调用sendSmsWorker方法。最后删除当前会话内的草稿短信。
比较重要的是两个方法,第一个是conv.ensureThreadId,另一个是sendSmsWorker,我们先来看一下conv.ensureThreadId这个方法:
/**For Dual Sim**/
public synchronized long ensureThreadId(int simId) {
if (DEBUG) {
LogTag.debug("ensureThreadId before: " + mThreadId);
}
if (mThreadId <= 0 || ((simId == 0 || simId == 1) && simId != mSimId)) {
mThreadId = getOrCreateThreadId(mContext, mRecipients, simId);
mSimId = simId;
}
if (DEBUG) {
LogTag.debug("ensureThreadId after: " + mThreadId);
}
return mThreadId;
}
/**End for Dual Sim**/
注意这个方法加了同步锁(synchronized),在这个方法中,我们看到,如果mThreadId不小于0,那么返回mThreadId,如果mThreadId小于等于0或者存在sim卡且simId不等于-1,那么,执行getOrCreateThreadId方法。
这个方法有两个参数,第一个参数为Context,重要的是第二个参数,为当前会话的联系人集合(保存了当前要发送的所有联系人)
/**For Dual Sim**/
private static long getOrCreateThreadId(Context context, ContactList list, int simId) {
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());
}
}
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, simId);
if (DELETEDEBUG || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
LogTag.debug("[Conversation] getOrCreateThreadId for (%s) returned %d",
recipients, retVal);
}
return retVal;
}
}
/**End for Dual Sim**/
在这个方法中,我们通过执行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,我们回到我们之前的地方, 在之前,我们得到了mThreadId,那么我们就可以继续往下走了
// 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);
继续看看在sendSmsWork方法中的逻辑:
WorkingMessage.java
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层。