高通android 7.0彩信发送流程

原创 2017年09月12日 08:59:53
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的关系,可以看博客
http://blog.csdn.net/tangyisen18/article/details/77977582

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

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

源码剖析之sun.misc.Unsafe

转载于:http://zeige.iteye.com/blog/1182571 首先介绍一下什么是Compare And Swap(CAS)?简单的说就是比较并交换。 CAS 操作包含三个...
  • zgmzyr
  • zgmzyr
  • 2013-05-08 21:52
  • 7763

这个年纪:齐一

这是一首歌,先献上歌词: 当我发现我已到了该成家的年纪 但我的女人呢 但我的女人呢 当我习惯把实话都变成了童话 那我的单纯呢 那我的单纯呢 这个年纪我已不再将就 有些事情无法强求 该来...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

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

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

数据结构之算法时间复杂度

数据结构之算法时间复杂度原文链接 算法的时间复杂度定义为:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也...

SQL两表之间:根据一个表的字段更新另一个表的字段

1. 写法轻松,更新效率高: update table1 set field1=table2.field1, field2=table2.field2 from table2 where ...

SQL——两个表之间的更新:用一个表的字段更新另一个表的字段

--1. 这种写法相当于一个 Left join, 以外面的where为更新条数。 -- 注意! 如果内面的表的(where之后取得的)条数比外面表的(where之后取得的)条数少, 会造成 不...

GitChat · 人工智能 | 用语音和自然语言控制智能家居——实例分享

GitChat 作者:晶晶郭 关注公众号:GitChat 技术杂谈,一本正经的讲技术 前言ZigBee作为一种短距离、低功耗的无线通信局域网协议,其优点是超低功耗、安全性高和自组网,并且可容纳多...

怎样将Oracle一张表的多个字段更新到另一张表中去

假设表a中有多个字段(province ,city)需要从b表获取(两张表的mobile一样),总结了几种写法。   一、update a  set a.province=(select prov...

使用SqlDataSource插入、更新以及删除数据

在概述插入、更新和删除数据中我们讨论过,GridView控件提供了内建的更新与删除功能,而DetailsView和FormView控件除了这些之外还拥有插入功能。这些数据修改功能可以直接接入到数据源控...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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