android短信源码部分解析(双卡)

点击新建短信之后

 @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层。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值