财富坊cff888
  • 删除
  • ComposeMessageActivity.java
    sendMessage

    WorkingMessage
    send

    private void prepareForSave(boolean notify) {
    ? ? ? ? // Make sure our working set of recipients is resolved
    ? ? ? ? // to first-class Contact objects before we save.
    ? ? ? ? syncWorkingRecipients();

    ? ? ? ? if (hasMmsContentToSave()) {
    ? ? ? ? ? ? ensureSlideshow();
    ? ? ? ? ? ? syncTextToSlideshow();
    ? ? ? ? }
    ? ? }
    因为private SlideshowModel mSlideshow;这个是跟数据库中part表数据对应的,所以现在要发送彩信,如果有内容,则要将其都添加到mSlideshow中。

    if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {

    注意,例如你现在添加了一张图片,则这个时候其不会马上存入数据库中,但是会存入到mSlideshow中,当短信发送或者保存为草稿的时候才写入到数据库中的。

    final PduPersister persister = PduPersister.getPduPersister(mActivity);
    彩信是依靠PduPersister来将彩信数据写入到数据库的。
    final SlideshowModel slideshow = mSlideshow;
    final CharSequence subject = mSubject;
    final boolean textOnly = mAttachmentType == TEXT;
    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();同样开启一个子线程来进行发送彩信

    private static SendReq makeSendReq(Conversation conv, CharSequence subject) {
    ? ? ? ? String[] dests = conv.getRecipients().getNumbers(true /* scrub for MMS address */);
    ? ? ? ? SendReq req = new SendReq();
    ? ? ? ? EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(dests);
    ? ? ? ? if (encodedNumbers != null) {
    ? ? ? ? ? ? req.setTo(encodedNumbers);//将收件人写入
    ? ? ? ? }
    ? ? ? ? if (!TextUtils.isEmpty(subject)) {
    ? ? ? ? ? ? req.setSubject(new EncodedStringValue(subject.toString()));//将主题写入
    ? ? ? ? }
    ? ? ? ? req.setDate(System.currentTimeMillis() / 1000L);//将当前时间写入
    ? ? ? ? return req;
    }

    void sendMmsWorker(Conversation conv, Uri mmsUri, PduPersister persister,
    ? ? ? ? ? ? SlideshowModel slideshow, SendReq sendReq, boolean textOnly)
    DraftCache.getInstance().setSavingDraft(true);设置保存草稿,好像是进行互斥,所以彩信是先要保存草稿,然后才进行发送
    mStatusListener.onPreMessageSent();//彩信发送前更新短信UI

    if (newMessage) {//此时为true
    ? ? ? ? ? ? ? ? // 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);
    ? ? ? ? ? ? ? ? }
    ? ? ? ? ? ? ? ? if ((TelephonyManager.getDefault().getPhoneCount()) > 1) {
    ? ? ? ? ? ? ? ? ? ? values.put(Mms.SUBSCRIPTION_ID, mCurrentConvSubId);
    ? ? ? ? ? ? ? ? } else {
    ? ? ? ? ? ? ? ? ? ? values.put(Mms.SUBSCRIPTION_ID,
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? SubscriptionManager.getDefaultDataSubscriptionId());
    ? ? ? ? ? ? ? ? }
    ? ? ? ? ? ? ? ? mmsUri = SqliteWrapper.insert(mActivity, mContentResolver, Mms.Outbox.CONTENT_URI,
    ? ? ? ? ? ? ? ? ? ? ? ? values);//插入到发件箱中
    ?}
    同样,彩信,pdu表中也有一个字段是表示当前彩信的各种状态的,就是msg_box字段,这里是发件箱,其类型type是PduHeaders.MESSAGE_TYPE_SEND_REQ,往数据库pdu的发件箱写入数据

    mStatusListener.onMessageSent();//短信发送后更新UI,此时显示正在发送中...

    然后查询所有处于发件箱的彩信,计算他们的附件总大小,看是否超出了限制(为什么这里有这个限制?)

    if (newMessage) {
    ? ? ? ? ? ? ? ? // Create a new MMS message if one hasn't been made yet.
    ? ? ? ? ? ? ? ? mmsUri = createDraftMmsMessage(persister, sendReq, slideshow, mmsUri,
    ? ? ? ? ? ? ? ? ? ? ? ? mActivity, null);//将mSlideshow的数据写入到part表中
    ? ? ? ? ? ? } else {
    ? ? ? ? ? ? ? ? // Otherwise, sync the MMS message in progress to disk.
    ? ? ? ? ? ? ? ? updateDraftMmsMessage(mmsUri, persister, slideshow, sendReq, null);//更新草稿
    ? ? ? ? ? ? }

    ContentValues values = new ContentValues(1);
    ? ? ? ? if ((TelephonyManager.getDefault().getPhoneCount()) > 1) {
    ? ? ? ? ? ? values.put(Mms.SUBSCRIPTION_ID, mCurrentConvSubId);
    ? ? ? ? } else {
    ? ? ? ? ? ? values.put(Mms.SUBSCRIPTION_ID, SubscriptionManager.getDefaultDataSubscriptionId());
    ? ? ? ? }
    ? ? ? ? SqliteWrapper.update(mActivity, mContentResolver, mmsUri, values, null, null);
    更新subId

    MessageSender sender = new MmsMessageSender(mActivity, mmsUri,
    ? ? ? ? ? ? ? ? slideshow.getCurrentMessageSize(), mCurrentConvSubId);
    调用MmsMessageSender的sendMessage来发送彩信

    MmsMessageSender.java
    sendMessage
    首先是继续更新数据库的一些数据
    PduPersister p = PduPersister.getPduPersister(mContext);
    ? ? ? ? GenericPdu pdu = p.load(mMessageUri);//从数据库中加载URI为mMessageUri到内存中,所以对于彩信,与数据库交互我们是直接用google提供的pdu,非常简单,这个另外一个专题讲。
    SendReq sendReq = (SendReq) pdu; 发送请求的数据类型

    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);
    ? ? ? ? }
    我们已经是处于发件箱中,所以走第一个分支,即在表pending_msgs中插入一条数据,表示是待发送彩信


    Intent intent = new Intent(mContext, TransactionService.class);
    ? ? ? ? intent.putExtra(Mms.SUBSCRIPTION_ID, mSubId);
    ? ? ? ? mContext.startService(intent);
    启动TransactionService服务来发送彩信。

    TransactionService.java
    onStartCommand
    第一次是EVENT_NEW_INTENT消息
    private ServiceHandler mServiceHandler;
    handleMessage
    onNewIntent((Intent)msg.obj, msg.arg1);
    mConnMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    发送彩信需要先检测ConnectivityManager服务,这个不是数据连接服务
    我们假设发送环境OK,可以发送彩信。
    Cursor cursor = PduPersister.getPduPersister(this).getPendingMessages(
    ? ? ? ? ? ? ? ? ? ? System.currentTimeMillis());
    加载所有的pending短信
    int columnIndexOfRetryIndex = cursor.getColumnIndexOrThrow(
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? PendingMessages.RETRY_INDEX);
    这个是重试次数
    transactionType是 Transaction.SEND_TRANSACTION;

    TransactionBundle args = new TransactionBundle(transactionType,
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uri.toString(), subId);
    此时uri是pdu的uri

    launchTransaction(serviceId, args, false);
    Message msg = mServiceHandler.obtainMessage(EVENT_TRANSACTION_REQUEST);

    case Transaction.SEND_TRANSACTION:
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? transaction = new SendTransaction(
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TransactionService.this, serviceId,
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? transactionSettings, args.getUri());
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
    if (!processTransaction(transaction)) {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? transaction = null;
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? return;
    ? ? ? ? ? ? ? ? ? ? ? ? }
    mPending和mProcessing此时是没有的

    beginMmsConnectivity(subId);开始mms的数据连接

    if (!mIsAvailable[phoneId]) {
    ? ? ? ? ? ? ? ? ? ? mPending.add(transaction);
    ? ? ? ? ? ? ? ? ? ? LogTag.debugD("processTransaction: connResult=APN_REQUEST_STARTED, " +
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? "defer transaction pending MMS connectivity");
    ? ? ? ? ? ? ? ? ? ? return true;
    ? ? ? ? ? ? ? ? }如果当前sim卡不可用,就会将这条发送记录添加到mPending中
    if (mProcessing.size() > 0) {
    ? ? ? ? ? ? ? ? ? ? LogTag.debugD("Adding transaction to 'mPending' list: " + transaction);
    ? ? ? ? ? ? ? ? ? ? mPending.add(transaction);
    ? ? ? ? ? ? ? ? ? ? return true;
    ? ? ? ? ? ? ? ? } else {
    ? ? ? ? ? ? ? ? ? ? LogTag.debugD("Adding transaction to 'mProcessing' list: " + transaction);
    ? ? ? ? ? ? ? ? ? ? mProcessing.add(transaction);
    ? ? ? ? ? ? ? ? }我们现在是走else,所以直接处理。所以这两个的关系是,mPending是将要处理,而mProcessing是正在处理的。
    transaction.attach(TransactionService.this);
    ? ? ? ? ? ? transaction.process();
    process走的是SendTransaction的process
    public void process() {
    ? ? ? ? mThread = new Thread(this, "SendTransaction");
    ? ? ? ? mThread.start();
    ? ? }
    所以启动子线程发送彩信,然后返回true,我们继续看主线程。哦,直接return。

    SendTransaction
    run

    RateController rateCtlr = RateController.getInstance();

    PduPersister persister = PduPersister.getPduPersister(mContext);
    ? ? ? ? ? ? SendReq sendReq = (SendReq) persister.load(mSendReqURI);
    从数据库加载要发送的彩信
    重新设置时间,这才是要发送的时间,然后更新数据库

    byte[] response = sendPdu(SendingProgressTokenManager.get(tokenKey),
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? new PduComposer(mContext, sendReq).make());

    Transaction.java
    ?/**
    ? ? ?* A common method to send a PDU to MMSC.
    ? ? ?*
    ? ? ?* @param token The token to identify the sending progress.
    ? ? ?* @param pdu A byte array which contains the data of the PDU.
    ? ? ?* @return A byte array which contains the response data.
    ? ? ?* ? ? ? ? If an HTTP error code is returned, an IOException will be thrown.
    ? ? ?* @throws IOException if any error occurred on network interface or
    ? ? ?* ? ? ? ? an HTTP error code(>=400) returned from the server.
    ? ? ?* @throws MmsException if pdu is null.
    ? ? ?*/根据注释,就是利用这个发送彩信的,pdu是发送的彩信内容,token是当前发送的一个唯一标识,而返回值则是发送结果
    protected byte[] sendPdu(long token, byte[] pdu) throws IOException, MmsException {
    ? ? ? ? return sendPdu(token, pdu, mTransactionSettings.getMmscUrl());
    ? ? }

    protected byte[] sendPdu(long token, byte[] pdu,
    ? ? ? ? ? ? String mmscUrl) throws IOException, MmsException {
    ? ? ? ? if (pdu == null) {
    ? ? ? ? ? ? throw new MmsException();
    ? ? ? ? }

    ? ? ? ? return HttpUtils.httpConnection(
    ? ? ? ? ? ? ? ? mContext, token,
    ? ? ? ? ? ? ? ? mmscUrl,
    ? ? ? ? ? ? ? ? pdu, HttpUtils.HTTP_POST_METHOD,
    ? ? ? ? ? ? ? ? mTransactionSettings.isProxySet(),
    ? ? ? ? ? ? ? ? mTransactionSettings.getProxyAddress(),
    ? ? ? ? ? ? ? ? mTransactionSettings.getProxyPort());
    ? ? }
    AndroidHttpClient往下看是利用AndroidHttpClient来发送。这个我们后面再看。

    String respStr = new String(response);
    我这里是发送失败,看起来是乱码

    SendConf conf = (SendConf) new PduParser(response,
    ? ? ? ? ? ? ? ? ? ?PduParserUtil.shouldParseContentDisposition()).parse();

    int respStatus = conf.getResponseStatus();//发送失败这里是130,public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82;
    ? ? ? ? ? ? values.put(Mms.RESPONSE_STATUS, respStatus);
    if (respStatus != PduHeaders.RESPONSE_STATUS_OK) {//不是发送成功就更新状态
    ? ? ? ? ? ? ? ? SqliteWrapper.update(mContext, mContext.getContentResolver(),
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?mSendReqURI, values, null, null);
    ? ? ? ? ? ? ? ? Log.e(TAG, "Server returned an error code: " + respStatus);
    ? ? ? ? ? ? ? ? return;
    ? ? ? ? ? ? }

    if (mTransactionState.getState() != TransactionState.SUCCESS) {
    ? ? ? ? ? ? ? ? mTransactionState.setState(TransactionState.FAILED);
    ? ? ? ? ? ? ? ? mTransactionState.setContentUri(mSendReqURI);
    ? ? ? ? ? ? ? ? Log.e(TAG, "Delivery failed.");
    ? ? ? ? ? ? }
    String messageId = PduPersister.toIsoString(conf.getMessageId());
    ? ? ? ? ? ? values.put(Mms.MESSAGE_ID, messageId);
    ? ? ? ? ? ? SqliteWrapper.update(mContext, mContext.getContentResolver(),
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?mSendReqURI, values, null, null);

    ? ? ? ? ? ? // Move M-Send.req from Outbox into Sent.
    ? ? ? ? ? ? Uri uri = persister.move(mSendReqURI, Sent.CONTENT_URI);//发送成功应该会将pending里面的条目删除了,应该在数据库中自动操作的。
    发送成功会将彩信状态从待发送转为已经发送

    notifyObservers();//这里会对监听者进行回调,TransactionService的update会被回调。这里应该是RetryScheduler的update被调用。都有。Transaction extends Observable提供了attach


    mProcessing.remove(transaction);
    else if (mProcessing.isEmpty()) {
    ? ? ? ? ? ? ? ? ? ? LogTag.debugD("update: endMmsConnectivity");
    ? ? ? ? ? ? ? ? ? ? endMmsConnectivity(transaction.getSubId());
    ? ? ? ? ? ? ? ? }关闭mms的数据连接

    Intent intent = new Intent(TRANSACTION_COMPLETED_ACTION);
    ? ? ? ? ? ? TransactionState state = transaction.getState();
    ? ? ? ? ? ? int result = state.getState();
    ? ? ? ? ? ? intent.putExtra(STATE, result);
    case TransactionState.FAILED:
    boolean failSetupDataCall = Transaction.FAIL_REASON_CAN_NOT_SETUP_DATA_CALL
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? == transaction.getFailReason();这个是??
    isLastRetry
    scheme.getRetryLimit()这里有5次

    if (type == Transaction.SEND_TRANSACTION) {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (failSetupDataCall) {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mToastHandler.sendEmptyMessage(
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TOAST_SETUP_DATA_CALL_FAILED_FOR_SEND);?<string name="no_network_send_failed_retry">"无网络,发送失败,稍后自动重新发送。"</string>
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } else {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mToastHandler.sendEmptyMessage(TOAST_SEND_FAILED_RETRY);<string name="send_failed_retry">"发送失败,稍后自动重新发送。"</string>
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
    sendBroadcast(intent);
    finally {
    ? ? ? ? ? ? transaction.detach(this);
    ? ? ? ? ? ? stopSelfIfIdle(serviceId);
    ? ? ? ? }

    TRANSACTION_COMPLETED_ACTION 没有发现哪个广播会接收。

    所以到这里结束了。UI会根据数据库的改变而刷新界面。

    彩信的重发机制
    跟DefaultRetryScheme这个有关
    <integer-array name="retry_scheme">
    ? ? ? ? <item>0</item>
    ? ? ? ? <item>60000</item>
    ? ? ? ? <item>300000</item>
    ? ? ? ? <item>600000</item>
    ? ? ? ? <item>1800000</item>
    ? ? </integer-array>


    getWaitingInterval

    RetryScheduler.java

    如果想了解彩信发送过程中与pdu的关系,可以看博客
    /bc5/tangyisen18/article/details/77977582

    如果发现本博客对您们有帮助,帮忙顶下。谢谢了!!

    阅读全文

    Android 发送彩信

    一、调用系统UI发送 Intent sendIntent = new Intent(Intent.ACTION_SEND, Uri.parse("mms://")); sendIntent.s...
    • thundercat
    • thundercat
    • 2012年02月16日 16:46
    • 2997

    Android Mms专题之:信息发送流程

    信息的发送,对于Mms应用程序来讲主要就是在信息数据库中创建并维护一条信息记录,真正的发送过程交由底层(Frameworks层)函数来处理。 总体的来讲,当信息创建完成后,对于信息通常有三个去处,一...
    • hitlion2008
    • hitlion2008
    • 2011年12月29日 22:22
    • 14304

    Android 收发彩信(MMS)

    前段时间(已经过去两个月了....)公司让搞一下android彩信的拦截与发送,于是就在网上找了一些资料,开始研究它的实现过程。 PS:需要从系统源码中扣取部分文件,大概在30个左右,不知道能不能精...
    • gf771115
    • gf771115
    • 2012年09月03日 14:50
    • 13138

    高通android 7.0彩信发送过程中使用到的google pdu

    对于彩信与数据库的交互操作,google并没有将这部分代码放在Mms中,而是放在framework中的pdu部分。 具体代码路径是在:opt\telephony\src\java\com\googl...
    • tangyisen18
    • tangyisen18
    • 2017年09月14日 10:42
    • 323

    高通Android 4.4 彩信发送流程

    MMS send flow in QCOM MMS发送流程图 Key Point 1:在点击短信界面的发送button(高通插入双卡后有两个botton)后,会进入ComposeMessag...
    • vvvvcp
    • vvvvcp
    • 2015年04月13日 11:16
    • 928

    【Android】Android彩信发送源码

    • 2013年04月07日 11:36
    • 1.3MB
    • 下载

    android彩信后台发送

    • 2013年06月19日 21:47
    • 607KB
    • 下载

    第74章、再识Intent-调用发送彩信程序(从零开始学Android)

    通过Intent完成彩信发送。 ? 一、设计界面   1、布局文件   打开res/layout/activity_main.xml文件。   输入以下代码: ...
    • jianghuiquan
    • jianghuiquan
    • 2013年03月07日 17:36
    • 3551

    ANDROID 7.0 mms发送及切换

    该篇针对上一篇的6.0数据切换过程。时序图如下: 该过程主要是切换过程。因为7.0的切换核心类发生很大变化,主要是体现PhoneSwitcher及TelephonyNetworkFactory新组成...
    • what_a_fuck
    • what_a_fuck
    • 2016年09月26日 14:37
    • 705

    【Android】Android 彩信发送的两种方式+源代码

    Android ?彩信发送的两种方式 第一种:直接调用彩信发送接口 实现代码如下, Intent intent = new Intent(Intent.ACTION_SEND); int...
    • dyllove98
    • dyllove98
    • 2013年04月21日 09:34
    • 1585
    内容举报
    返回顶部
    收藏助手
    不良信息举报
    您举报文章:高通android 7.0彩信发送流程
    举报原因:
    原因补充:

    (最多只允许输入30个字)