深度分析:Android4.3下MMS发送到附件为音频文件(音频为系统内置音频)的彩信给自己,添加音频-发送彩信-接收彩信-下载音频附件-预览-播放(二,发送彩信<1>)

来源:互联网 发布:剑三好友招募积分算法 编辑:程序博客网 时间:2024/04/30 22:05

当准备工作(添加附件,输入文本内容)完成之后,我们这里开始进行该流程分析的第二阶段,也就是发送彩信。这里我们从ComposeMessageActivity类的点击发送按钮(mSendButtonMms)的点击事件开始:<TAG 1-1>

    @Override
    public void onClick(View v) {
        if (mShowTwoButtons && (v == mSendButtonSmsViewSec || v == mSendButtonMmsViewSec)
                && isPreparedForSending()) {
            confirmSendMessageIfNeeded(MSimConstants.SUB2);
        } else if ((v == mSendButtonSms || v == mSendButtonMms) && isPreparedForSending()) {
            if (mShowTwoButtons) {
                confirmSendMessageIfNeeded(MSimConstants.SUB1);
            } else {
                confirmSendMessageIfNeeded();
            }
        } else if ((v == mRecipientsPicker)) {
            launchMultiplePhonePicker();
        } else if ((v == mRecipientsPickerGroups)) {
            launchContactGroupPicker();
        } else if (v == mAttachButton) {
            showAddAttachmentDialog(false);
        }
    }

上述<TAG 1-1>调用了confirmSendMessageIfNeeded方法,这里对收件人(mRecipientEditor)进行了一些处理;<TAG 1-2>

    private void confirmSendMessageIfNeeded() {
        boolean isMms = mWorkingMessage.requiresMms();
        if (!isRecipientsEditorVisible()) {
            if (MessageUtils.isMobileDataDisabled(this) &&
                    MessageUtils.CAN_SETUP_MMS_DATA && isMms) {
                showMobileDataDisabledDialog();
            } else if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
                sendMsimMessage(true);
            } else {
                sendMessage(true);
            }
            return;
        }

        if (mRecipientsEditor.hasInvalidRecipient(isMms)) {
            showInvalidRecipientDialog();
        } else if (MessageUtils.isMobileDataDisabled(this) &&//判断数据连接是否正常
                MessageUtils.CAN_SETUP_MMS_DATA && isMms) {
            showMobileDataDisabledDialog();
        } 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();
            if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
                sendMsimMessage(true);
            } else {
                sendMessage(true);
            }
        }
    }
上述代码<TAG 1-2>中,调用了ComposeMessageActivity类的sendMessage方法;<TAG 1-3>
    private void sendMessage(boolean bCheckEcmMode) {
        // Check message size, if >= max message size, do not send message.
        if(checkMessageSizeExceeded()){
            return;
        }

        // If message is sent make the mIsMessageChanged is true
        // when activity is from SearchActivity.
        mIsMessageChanged = mIsFromSearchActivity;
        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();


            if (mWorkingMessage.getResendMultiRecipients()) {
                //if resend sms recipient is more than one, use mResendSmsRecipient
                mWorkingMessage.send(mResendSmsRecipient);
            } else {
                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();
        }
    }

上述代码<TAG 1-3>中,调用了WorkingMessage类中的send()方法;<TAG 1-4>
 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)) {
            // 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.
            // add user's signature first if this feature is enabled.
            String text = mText.toString();
            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mActivity);
            if (sp.getBoolean("pref_key_enable_signature", false)) {
                String signature = (sp.getString("pref_key_edit_signature", "")).trim();
                if (signature.length() > 0) {
                    String sigBlock = "\n" + signature;
                    if (!text.endsWith(sigBlock)) {
                        // Signature should be written behind the text in a
                        // newline while the signature has changed.
                        text += sigBlock;
                    }
                }
            }
            final String msgText = text;
            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;
    }

上述代码<TAG 1-4>中,生成了彩信的可发送的Pdu—SendReq,接着调用了sendMmsWorker()接着会把彩信写入数据库,把要发送的SendReq也会写入数据库,后面会再从数据库中读取出SendReq,并标识为草稿;然后会构建MmsMessageSender,传入收信人和彩信的Uri,让其发送。这期间也会回调UI一次,以初始化收信人编辑框和信息编辑框<TAG 1-5>

private void sendMmsWorker(Conversation conv, Uri mmsUri, PduPersister persister,
            SlideshowModel slideshow, SendReq sendReq, boolean textOnly) {
        long threadId = 0;
        Cursor cursor = null;
        boolean newMessage = false;
        boolean forwardMessage = conv.getHasMmsForward();
        boolean sameRecipient = false;
        ContactList contactList = conv.getRecipients();
        if (contactList != null) {
            String[] numbers = contactList.getNumbers();
            String[] forward = conv.getForwardRecipientNumber();
            if (numbers != null && forward != null
                    && (numbers.length == forward.length)) {
                List<String> currentNumberList = Arrays.asList(numbers);
                List<String> forwardNumberList = Arrays.asList(forward);
                Collections.sort(currentNumberList);
                Collections.sort(forwardNumberList);
                if (currentNumberList.equals(forwardNumberList)) {
                    sameRecipient = true;
                }
            }
        }
        try {
            // Put a placeholder message in the database first
            DraftCache.getInstance().setSavingDraft(true);
            mStatusListener.onPreMessageSent();

            // Make sure we are still using the correct thread ID for our
            // recipient set.
            threadId = conv.ensureThreadId();
            if (forwardMessage && sameRecipient) {
                MessageUtils.sSameRecipientList.add(threadId);
            }

            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
                LogTag.debug("sendMmsWorker: update draft MMS message " + mmsUri +
                        " threadId: " + threadId);
            }

            // One last check to verify the address of the recipient.
            String[] dests = conv.getRecipients().getNumbers(true /* scrub for MMS address */);
            if (dests.length == 1) {
                // verify the single address matches what's in the database. If we get a different
                // address back, jam the new value back into the SendReq.
                String newAddress =
                    Conversation.verifySingleRecipient(mActivity, conv.getThreadId(), dests[0]);

                if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
                    LogTag.debug("sendMmsWorker: newAddress " + newAddress +
                            " dests[0]: " + dests[0]);
                }

                if (!newAddress.equals(dests[0])) {
                    dests[0] = newAddress;
                    EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(dests);
                    if (encodedNumbers != null) {
                        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
                            LogTag.debug("sendMmsWorker: REPLACING number!!!");
                        }
                        sendReq.setTo(encodedNumbers);
                    }
                }
            }
            newMessage = mmsUri == null;
            if (newMessage) {
                // Write something in the database so the new message will appear as sending
                ContentValues values = new ContentValues();
                values.put(Mms.MESSAGE_BOX, Mms.MESSAGE_BOX_OUTBOX);
                values.put(Mms.THREAD_ID, threadId);
                values.put(Mms.MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);
                if (textOnly) {
                    values.put(Mms.TEXT_ONLY, 1);
                }
                mmsUri = SqliteWrapper.insert(mActivity, mContentResolver, Mms.Outbox.CONTENT_URI,
                        values);
            }
            mStatusListener.onMessageSent();

            // If user tries to send the message, it's a signal the inputted text is
            // what they wanted.
            UserHappinessSignals.userAcceptedImeText(mActivity);

            // First make sure we don't have too many outstanding unsent message.
            cursor = SqliteWrapper.query(mActivity, mContentResolver,
                    Mms.Outbox.CONTENT_URI, MMS_OUTBOX_PROJECTION, null, null, null);
            if (cursor != null) {
                long maxMessageSize = MmsConfig.getMaxSizeScaleForPendingMmsAllowed() *
                MmsConfig.getMaxMessageSize();
                long totalPendingSize = 0;
                while (cursor.moveToNext()) {
                    totalPendingSize += cursor.getLong(MMS_MESSAGE_SIZE_INDEX);
                }
                if (totalPendingSize >= maxMessageSize) {
                    unDiscard();    // it wasn't successfully sent. Allow it to be saved as a draft.
                    mStatusListener.onMaxPendingMessagesReached();
                    markMmsMessageWithError(mmsUri);
                    return;
                }
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        try {
            if (newMessage) {
                // Create a new MMS message if one hasn't been made yet.
                mmsUri = createDraftMmsMessage(persister, sendReq, slideshow, mmsUri,
                        mActivity, null);
            } else {
                // Otherwise, sync the MMS message in progress to disk.
                updateDraftMmsMessage(mmsUri, persister, slideshow, sendReq, null);
            }

            // Be paranoid and clean any draft SMS up.
            deleteDraftSmsMessage(threadId);
        } finally {
            DraftCache.getInstance().setSavingDraft(false);
        }

        // Resize all the resizeable attachments (e.g. pictures) to fit
        // in the remaining space in the slideshow.
        /*int error = 0;
        try {
            slideshow.finalResize(mmsUri);
        } catch (ExceedMessageSizeException e1) {
            error = MESSAGE_SIZE_EXCEEDED;
        } catch (MmsException e1) {
            error = UNKNOWN_ERROR;
        }
        if (error != 0) {
            markMmsMessageWithError(mmsUri);
            mStatusListener.onAttachmentError(error);
            return;
        }*/

        ContentValues values = new ContentValues(1);
        if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
            values.put(Mms.SUB_ID, mCurrentConvSub);
        } else {
           values.put(Mms.SUB_ID, MSimTelephonyManager.getDefault().getPreferredDataSubscription());
        }
        SqliteWrapper.update(mActivity, mContentResolver, mmsUri, values, null, null);

        MessageSender sender = new MmsMessageSender(mActivity, mmsUri,
                slideshow.getCurrentMessageSize(), mCurrentConvSub);
        try {
            if (!sender.sendMessage(threadId)) {
                // The message was sent through SMS protocol, we should
                // delete the copy which was previously saved in MMS drafts.
                SqliteWrapper.delete(mActivity, mContentResolver, mmsUri, null, null);
            }
            // After send mms, this thread should't have draft left.
            // Be paranoid and clean any draft MMS up.
            deleteDraftMmsMessage(threadId);

            // Make sure this thread isn't over the limits in message count
            Recycler.getMmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId);
        } catch (Exception e) {
            Log.e(TAG, "Failed to send message: " + mmsUri + ", threadId=" + threadId, e);
        }
        if (forwardMessage && sameRecipient) {
            MessageUtils.sSameRecipientList.remove(threadId);
        }
        MmsWidgetProvider.notifyDatasetChanged(mActivity);
        updateSearchResult();
    }

上述代码<TAG 1-5>中,创建MmsMessageSender实例并调用了sendMessage()方法;MmsMessageSender先从数据库中读出彩信发送的Pdu—SendReq,Google的内置包com.google.android.mms.*;里面封装了所有操作Pdu的方法,包括把Pdu写入数据库(PduPersister.persist()),从数据库中读取生成Pdu(PduPersister.load())。然后根据当前彩信的配置和其他信息对SendReq进行更新,比如设置Expiration,Priority,Date和Size等,把彩信移到Outbox,然后启动TransactionService来处理彩信。sendMessage()就此返回。WorkingMessage会再次回调UI的接口,因为此时彩信已被在数据库中,所以UI会刷新信息列表,显示刚刚的彩信,这时的状态应该是正在发送中。

    public boolean sendMessage(long token) throws MmsException {
        // Load the MMS from the message uri
        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
            LogTag.debug("sendMessage uri: " + mMessageUri);
        }
        PduPersister p = PduPersister.getPduPersister(mContext);
        GenericPdu pdu = p.load(mMessageUri);

        if (pdu.getMessageType() != PduHeaders.MESSAGE_TYPE_SEND_REQ) {
            throw new MmsException("Invalid message: " + pdu.getMessageType());
        }

        SendReq sendReq = (SendReq) pdu;

        // Update headers.
        updatePreferencesHeaders(sendReq);

        // MessageClass.
        sendReq.setMessageClass(DEFAULT_MESSAGE_CLASS.getBytes());

        // Update the 'date' field of the message before sending it.
        sendReq.setDate(System.currentTimeMillis() / 1000L);

        sendReq.setMessageSize(mMessageSize);

        p.updateHeaders(mMessageUri, sendReq);

        long messageId = ContentUris.parseId(mMessageUri);

        // Move the message into MMS Outbox.
        if (!mMessageUri.toString().startsWith(Mms.Draft.CONTENT_URI.toString())) {
            // If the message is already in the outbox (most likely because we created a "primed"
            // message in the outbox when the user hit send), then we have to manually put an
            // entry in the pending_msgs table which is where TransacationService looks for
            // messages to send. Normally, the entry in pending_msgs is created by the trigger:
            // insert_mms_pending_on_update, when a message is moved from drafts to the outbox.
            ContentValues values = new ContentValues(7);

            values.put(PendingMessages.PROTO_TYPE, MmsSms.MMS_PROTO);
            values.put(PendingMessages.MSG_ID, messageId);
            values.put(PendingMessages.MSG_TYPE, pdu.getMessageType());
            values.put(PendingMessages.ERROR_TYPE, 0);
            values.put(PendingMessages.ERROR_CODE, 0);
            values.put(PendingMessages.RETRY_INDEX, 0);
            values.put(PendingMessages.DUE_TIME, 0);

            SqliteWrapper.insert(mContext, mContext.getContentResolver(),
                    PendingMessages.CONTENT_URI, values);
        } else {
            p.move(mMessageUri, Mms.Outbox.CONTENT_URI);
        }

        // Start MMS transaction service
        SendingProgressTokenManager.put(messageId, token);
        Intent intent = new Intent(mContext, TransactionService.class);
        intent.putExtra(Mms.SUB_ID, mSubscription); //destination sub id
        intent.putExtra(MultiSimUtility.ORIGIN_SUB_ID,
                MultiSimUtility.getCurrentDataSubscription(mContext));
        if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {

            Intent silentIntent = new Intent(mContext,
                    com.android.mms.ui.SelectMmsSubscription.class);
            silentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            silentIntent.putExtras(intent); //copy all extras
            mContext.startService(silentIntent);
        } else {
            mContext.startService(intent);
        }

        return true;
    }










1 0