Android9.0 Mms (彩信接收步骤,Android9.0版本)

1.彩信接收步骤

我们首先看4个类分别是PDU重要的几个类


PduPersister       用于管理PDU存储
PduParser           用于解析PDU
PduComposer    用于生成PDU

关键的方法:
PduPersister    类
PduPersister  getPduPersister(Context)    Get the object
Uri   persist(GenericPdu, Uri)   把一个GenericPdu保存到Uri所指定的数据库中,返回指向新生成数据的Uri  ,获取的数据也                                                         可以保存到数据库中
GenericPdu  load(Uri)  从数据库把Uri所指的数据加载出来成一个GenericPdu对象
Uri   move(Uri, Uri)   把Pdu从一个地方移到另一个地方,比如从草稿箱移动到发件箱,当MMS已发送时。
   
为什么会要把PDU的存储也封装成PduPersister呢?因为PDU的存储方式 是放在标准的SQLiteDatabase中,通过TelephonyProvider,而SQLiteDatabase中存储不能以直接的PDU的字节流来存储,必须要把PDU拆解成为可读的字段,因此在存储PDU和从存储加载PDU的过程 中涉及到PDU数据上面的处理,因此封装出来,更方便使用。

PduParser:用于把PDU字节流解析成为Android可识别的GenericPdu
PduParser    PduParser(byte[])    Construct an object
GenericPdu    parse()    Parse the PDU byte stream into Android PDU GenericPdu

 
PduComposer:把GenericPdu打包生成PDU字节流

Return    Method    Description
PduComposer    PduComposer(Context, GenericPdu)    Construct an object
byte[]    make()    Transfer the GenericPdu into a PDU byte stream

开始看代码

首先收到彩信是底层发送一个广播告诉上层的Mms,

<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />

PushReceiver.java   这个是一个广播

    @Override
    public void onReceive(Context context, Intent intent) {
        if (!MessageUtils.hasBasicPermissions()){
            Log.d(TAG, "PushReceiver do not have basic permissions");
            return;
        }
        mContext=context;

        if (intent.getAction().equals(WAP_PUSH_DELIVER_ACTION)
                && (ContentType.MMS_MESSAGE.equals(intent.getType())
                || WAP_PUSH_TYPE_SIC.equals(intent.getType())
                || WAP_PUSH_TYPE_SLC.equals(intent.getType())))  {
            if (LOCAL_LOGV) {
                Log.v(TAG, "Received PUSH Intent: " + intent);
            }


            // Hold a wake lock for 5 seconds, enough to give any
            // services we start time to take their own wake locks.
            PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                                            "MMS PushReceiver");
            wl.acquire(5000);
            new ReceivePushTask(context).execute(intent); //开启异步进行下载
        }
    }

主要看收到彩信的一个Intent

收到彩信时 Intent
Intent { 
    act=android.provider.Telephony.WAP_PUSH_DELIVER 
    typ=application/vnd.wap.mms-message 
    flg=0x18000010 
    cmp=com.android.mms/.transaction.PushReceiver
     (has extras)
 }

开启异步

    private class ReceivePushTask extends AsyncTask<Intent,Void,Void> {
        private Context mContext;
        public ReceivePushTask(Context context) {
            mContext = context;
        }


    int hexCharToInt(char c) {
        if (c >= '0' && c <= '9') return (c - '0');
        if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
        if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
    }


    public String bytesToHexString(byte[] bytes) {
        if (bytes == null) return null;

        StringBuilder ret = new StringBuilder(2*bytes.length);

        for (int i = 0 ; i < bytes.length ; i++) {
            int b;

            b = 0x0f & (bytes[i] >> 4);

            ret.append("0123456789abcdef".charAt(b));

            b = 0x0f & bytes[i];

            ret.append("0123456789abcdef".charAt(b));
        }

        return ret.toString();
    }


        @Override
        protected Void doInBackground(Intent... intents) {
            Intent intent = intents[0];

            // Get raw PDU push-data from the message and parse it
            byte[] pushData = intent.getByteArrayExtra("data");
            if (DEBUG) {
                Log.d(TAG, "PushReceive: pushData= " + bytesToHexString(pushData));
            }
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
            boolean wapPushEnabled = prefs.getBoolean(WAP_PUSH_MESSAGE, true);

            if (wapPushEnabled && (WAP_PUSH_TYPE_SIC.equals(intent.getType())
                    || WAP_PUSH_TYPE_SLC.equals(intent.getType()))) {
                ByteArrayInputStream bais = new ByteArrayInputStream(pushData);
                try {
                    Class mWapPushHandler = Class.forName("com.qrd.wappush.WapPushHandler");
                    Object WapPushHandlerObj = mWapPushHandler.newInstance();
                    Method mHandleWapPush = mWapPushHandler.getDeclaredMethod("handleWapPush",
                            InputStream.class, String.class, Context.class,
                            int.class, String.class);
                    Method mGetThreadID = mWapPushHandler.getDeclaredMethod("getThreadID");
                    String address = intent.getStringExtra("address");
                    if (address == null) {
                        address = WAP_PUSH_DEFAULT_ADDRESS;
                    }
                    Uri pushMsgUri = (Uri)mHandleWapPush.invoke(WapPushHandlerObj, bais,
                            intent.getType(), mContext,
                            intent.getIntExtra(ConstantsWrapper.Phone.PHONE_KEY, 0),
                            address + WAP_PUSH);

                    if (pushMsgUri != null) {
                        // Called off of the UI thread so ok to block.
                        Recycler.getSmsRecycler().deleteOldMessagesByThreadId(
                            mContext.getApplicationContext(),
                            (Long)mGetThreadID.invoke(WapPushHandlerObj));
                        MessagingNotification.blockingUpdateNewMessageIndicator(
                            mContext, (Long)mGetThreadID.invoke(WapPushHandlerObj), false);
                        MmsWidgetProvider.notifyDatasetChanged(mContext);
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Wap Push Hander Error :" + e);
                }

                return null;
            }
            PduParser parser = new PduParser(pushData,
                    PduParserUtil.shouldParseContentDisposition());
            GenericPdu pdu = parser.parse();

            if (null == pdu) {
                Log.e(TAG, "Invalid PUSH data");
                return null;
            }

            PduPersister p = PduPersister.getPduPersister(mContext);
            ContentResolver cr = mContext.getContentResolver();
            int type = pdu.getMessageType();
            long threadId = -1;

            try {
                switch (type) {
                    case MESSAGE_TYPE_DELIVERY_IND://143表示消息已送达,送达报告
                    case MESSAGE_TYPE_READ_ORIG_IND: {
                        threadId = findThreadId(mContext, pdu, type);
                        if (threadId == -1) {
                            // The associated SendReq isn't found, therefore skip
                            // processing this PDU.
                            break;
                        }
                        Message me = new Message();
                        me.arg1=type;
                        mHandler.sendMessage(me);//送达报告发送
                        Uri uri = p.persist(pdu, Inbox.CONTENT_URI, true,
                                MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext), null);
                        // Update thread ID for ReadOrigInd & DeliveryInd.
                        ContentValues values = new ContentValues(1);
                        values.put(Mms.THREAD_ID, threadId);
                        SqliteWrapper.update(mContext, cr, uri, values, null, null);
                        break;
                    }
                    case MESSAGE_TYPE_NOTIFICATION_IND: {//收到消息调用
                        NotificationInd nInd = (NotificationInd) pdu;

                        if (MmsConfig.getTransIdEnabled()) {
                            byte [] contentLocation = nInd.getContentLocation();
                            if ('=' == contentLocation[contentLocation.length - 1]) {//--
                                byte [] transactionId = nInd.getTransactionId();
                                byte [] contentLocationWithId = new byte [contentLocation.length
                                                                          + transactionId.length];
                                System.arraycopy(contentLocation, 0, contentLocationWithId,
                                        0, contentLocation.length);
                                System.arraycopy(transactionId, 0, contentLocationWithId,
                                        contentLocation.length, transactionId.length);
                                nInd.setContentLocation(contentLocationWithId);
                            }
                        }

                        if (!isDuplicateNotification(mContext, nInd)) {//--
                            int subId = intent.getIntExtra(ConstantsWrapper.Phone.SUBSCRIPTION_KEY, 0);
                            //Phone ID will be updated in data base
                            Log.d(TAG, "PushReceiver subId : " + subId);
                            ContentValues values = new ContentValues(1);
                            values.put(Mms.SUBSCRIPTION_ID, subId);
                            Uri uri = p.persist(pdu, Inbox.CONTENT_URI,
                                    true,
                                    MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext),
                                    null);
                            SqliteWrapper.update(mContext, cr, uri, values, null, null);
                            String address = pdu.getFrom().getString();
                            threadId = MessagingNotification.getThreadId(mContext, uri);
                            MessageUtils
                                    .markAsNotificationThreadIfNeed(mContext, threadId, address);

                            if (!DownloadManager.getInstance().isAuto() &&
                                    !MessageUtils.isMobileDataEnabled(mContext, subId)) {
                                MessagingNotification.blockingUpdateNewMessageIndicator(mContext,
                                        threadId, false);
                                break;
                            }
                            Intent svc = new Intent(mContext, TransactionService.class);
                            svc.putExtra(TransactionBundle.URI, uri.toString());
                            svc.putExtra(TransactionBundle.TRANSACTION_TYPE,
                                    Transaction.NOTIFICATION_TRANSACTION);
                            svc.putExtra(Mms.SUBSCRIPTION_ID, subId);
                            mContext.startService(svc);
                        } else if (LOCAL_LOGV) {
                            Log.v(TAG, "Skip downloading duplicate message: "
                                    + new String(nInd.getContentLocation()));
                        }
                        break;
                    }
                    default:
                        Log.e(TAG, "Received unrecognized PDU.");
                }
            } catch (MmsException e) {
                Log.e(TAG, "Failed to save the data from PUSH: type=" + type, e);
            } catch (RuntimeException e) {
                Log.e(TAG, "Unexpected RuntimeException.", e);
            }

            if (LOCAL_LOGV) {
                Log.v(TAG, "PUSH Intent processed.");
            }

            return null;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }
    }

主要看

Uri uri = p.persist(pdu, Inbox.CONTENT_URI,
        true,
        MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext),
        null);

p是一个PduPersister  

p.persist();, 这里的操作是进行插入数据PDU表的数据  pdu里面自带一些协议

PduPersister.java


    public Uri persist(GenericPdu pdu, Uri uri, boolean createThreadId, boolean groupMmsEnabled,
            HashMap<Uri, InputStream> preOpenedFiles)
            throws MmsException {
        if (uri == null) {
            throw new MmsException("Uri may not be null.");
        }
        long msgId = -1;
        try {
            msgId = ContentUris.parseId(uri);
        } catch (NumberFormatException e) {
            // the uri ends with "inbox" or something else like that
        }
        boolean existingUri = msgId != -1;

        if (!existingUri && MESSAGE_BOX_MAP.get(uri) == null) {
            throw new MmsException(
                    "Bad destination, must be one of "
                    + "content://mms/inbox, content://mms/sent, "
                    + "content://mms/drafts, content://mms/outbox, "
                    + "content://mms/temp.");
        }
        synchronized(PDU_CACHE_INSTANCE) {
         
            if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
                if (LOCAL_LOGV) {
                    Log.v(TAG, "persist: " + uri + " blocked by isUpdating()");
                }
                try {
                    PDU_CACHE_INSTANCE.wait();
                } catch (InterruptedException e) {
                    Log.e(TAG, "persist1: ", e);
                }
            }
        }
        PDU_CACHE_INSTANCE.purge(uri);

        PduHeaders header = pdu.getPduHeaders();
        PduBody body = null;
        ContentValues values = new ContentValues();
        Set<Entry<Integer, String>> set;

        set = ENCODED_STRING_COLUMN_NAME_MAP.entrySet();
        for (Entry<Integer, String> e : set) {
            int field = e.getKey();
            EncodedStringValue encodedString = header.getEncodedStringValue(field);
            if (encodedString != null) {
                String charsetColumn = CHARSET_COLUMN_NAME_MAP.get(field);
                values.put(e.getValue(), toIsoString(encodedString.getTextString()));
                values.put(charsetColumn, encodedString.getCharacterSet());
            }
        }

        set = TEXT_STRING_COLUMN_NAME_MAP.entrySet();
        for (Entry<Integer, String> e : set){
            byte[] text = header.getTextString(e.getKey());
            if (text != null) {
                values.put(e.getValue(), toIsoString(text));
            }
        }

        set = OCTET_COLUMN_NAME_MAP.entrySet();
        for (Entry<Integer, String> e : set){
            int b = header.getOctet(e.getKey());
            if (b != 0) {
                values.put(e.getValue(), b);
            }
        }

        set = LONG_COLUMN_NAME_MAP.entrySet();
        for (Entry<Integer, String> e : set){
            long l = header.getLongInteger(e.getKey());
            if (l != -1L) {
                values.put(e.getValue(), l);
            }
        }

        HashMap<Integer, EncodedStringValue[]> addressMap =
                new HashMap<Integer, EncodedStringValue[]>(ADDRESS_FIELDS.length);
        // Save address information.
        for (int addrType : ADDRESS_FIELDS) {
            EncodedStringValue[] array = null;
            if (addrType == PduHeaders.FROM) {
                EncodedStringValue v = header.getEncodedStringValue(addrType);
                if (v != null) {
                    array = new EncodedStringValue[1];
                    array[0] = v;
                }
            } else {
                array = header.getEncodedStringValues(addrType);
            }
            addressMap.put(addrType, array);
        }

        HashSet<String> recipients = new HashSet<String>();
        int msgType = pdu.getMessageType();

        if ((msgType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND)
                || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
                || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
            switch (msgType) {
                case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
                case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
                    loadRecipients(PduHeaders.FROM, recipients, addressMap, false);

                    if (groupMmsEnabled) {
                        loadRecipients(PduHeaders.TO, recipients, addressMap, true);

                        loadRecipients(PduHeaders.CC, recipients, addressMap, true);
                    }
                    break;
                case PduHeaders.MESSAGE_TYPE_SEND_REQ:
                    loadRecipients(PduHeaders.TO, recipients, addressMap, false);
                    break;
            }
            long threadId = 0;
            if (createThreadId && !recipients.isEmpty()) {

                threadId = Threads.getOrCreateThreadId(mContext, recipients);
            }
            values.put(Mms.THREAD_ID, threadId);
        }

        long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.

        boolean textOnly = true;

        int messageSize = 0;
        if (pdu instanceof MultimediaMessagePdu) {
            body = ((MultimediaMessagePdu) pdu).getBody();
            // Start saving parts if necessary.
            if (body != null) {
                int partsNum = body.getPartsNum();
                if (partsNum > 2) {
                    textOnly = false;
                }
                for (int i = 0; i < partsNum; i++) {
                    PduPart part = body.getPart(i);
                    messageSize += part.getDataLength();
                    persistPart(part, dummyId, preOpenedFiles);
                    String contentType = getPartContentType(part);
                    if (contentType != null && !ContentType.APP_SMIL.equals(contentType)
                            && !ContentType.TEXT_PLAIN.equals(contentType)) {
                        textOnly = false;
                    }
                }
            }
        }

        values.put(Mms.TEXT_ONLY, textOnly ? 1 : 0);

        if (values.getAsInteger(Mms.MESSAGE_SIZE) == null) {
            values.put(Mms.MESSAGE_SIZE, messageSize);
        }

        Uri res = null;
        if (existingUri) {
            res = uri;
            SqliteWrapper.update(mContext, mContentResolver, res, values, null, null);
        } else {
               //Uri对应如果是Pdu表就是插入pdu如果是part表就更新part表
            res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);   //解析Byte数字后进行插入到PDU表或part表
            if (res == null) {
                throw new MmsException("persist() failed: return null.");
            }
            msgId = ContentUris.parseId(res);
        }

        values = new ContentValues(1);
        values.put(Part.MSG_ID, msgId);
        SqliteWrapper.update(mContext, mContentResolver,
                             Uri.parse("content://mms/" + dummyId + "/part"),
                             values, null, null);

        if (!existingUri) {
            res = Uri.parse(uri + "/" + msgId);
        }

        // Save address information.
        for (int addrType : ADDRESS_FIELDS) {
            EncodedStringValue[] array = addressMap.get(addrType);
            if (array != null) {
                persistAddress(msgId, addrType, array);
            }
        }

        return res;
    }

mmssms.db中PDU表经过 persister.persist方法PDU是插入了数据的


在这一步进行只是告诉APP有一条彩信等待这去下载


下载步骤是开启TransactionService.java

PushReceiver.java 

  // Start service to finish the notification transaction.
  Intent svc = new Intent(mContext, TransactionService.class);
  svc.putExtra(TransactionBundle.URI, uri.toString());
  svc.putExtra(TransactionBundle.TRANSACTION_TYPE,
                 Transaction.NOTIFICATION_TRANSACTION);
  svc.putExtra(Mms.SUBSCRIPTION_ID, subId);
  mContext.startService(svc);

TransactionService.java

这里主要看handler


        @Override
        public void handleMessage(Message msg) {
            LogTag.debugD("Handling incoming message: " + msg + " = " + decodeMessage(msg));

            Transaction transaction = null;

            switch (msg.what) {
                case EVENT_NEW_INTENT:
                    onNewIntent((Intent)msg.obj, msg.arg1);
                    break;

                case EVENT_QUIT:
                    getLooper().quit();
                    return;

                case EVENT_TRANSACTION_REQUEST:   //通知彩信接收
                    int serviceId = msg.arg1;
                    try {

                        TransactionBundle args = (TransactionBundle) msg.obj;
                        TransactionSettings transactionSettings;

                        LogTag.debugD("EVENT_TRANSACTION_REQUEST MmscUrl=" +
                                args.getMmscUrl() + " proxy port: " + args.getProxyAddress());

                        // Set the connection settings for this transaction.
                        // If these have not been set in args, load the default settings.
                        String mmsc = args.getMmscUrl();
                        if (mmsc != null) {
                            transactionSettings = new TransactionSettings(
                                    mmsc, args.getProxyAddress(), args.getProxyPort());
                        } else {
                            transactionSettings = new TransactionSettings(
                                                    TransactionService.this, null,
                                                    args.getSubId());
                        }

                        int transactionType = args.getTransactionType();


                        // Create appropriate transaction
                        switch (transactionType) {
                            case Transaction.NOTIFICATION_TRANSACTION:
                                String uri = args.getUri();
                                //用户通知
                                if (uri!=null){
                                long threadId = MessagingNotification.getThreadId(
                                        getApplicationContext(), Uri.parse(uri));
                                MessagingNotification.blockingUpdateNewMessageIndicator(getApplicationContext(),
                                        threadId,
                                        false);
                                MessagingNotification.updateDownloadFailedNotification(getApplicationContext());
                                MessageUtils.updateThreadAttachTypeByThreadId(getApplicationContext(), threadId);
                            
                                new AsyncHandler(getContentResolver(),threadId).startQuery(0,
                                        null,mAllCanonical,mProjection,"_id="+threadId,null,null);
                                    if (!SharePreferencesUtils.getBoolean(getApplicationContext(),SMART_CAT_AGREEMENT)) {
                                        SharePreferencesUtils.putBoolean(getApplicationContext(),SMART_CAT_AGREEMENT,true);
                                    }
                                }

                                if (uri != null) {
                                    //开始解压消息   自动解压彩信
                                    transaction = new NotificationTransaction(
                                            TransactionService.this, serviceId,
                                            transactionSettings, uri
                                            );
                                }  else {
                                    byte[] pushData = args.getPushData();
                                    PduParser parser = new PduParser(pushData,
                                            PduParserUtil.shouldParseContentDisposition());
                                    GenericPdu ind = parser.parse();

                                    int type = PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
                                    if ((ind != null) && (ind.getMessageType() == type)) {
                                        transaction = new NotificationTransaction(
                                                TransactionService.this, serviceId,
                                                transactionSettings, (NotificationInd) ind);
                                    } else {
                                        Log.e(TAG, "Invalid PUSH data.");
                                        transaction = null;
                                        return;
                                    }
                                }
                                break;
                            case Transaction.RETRIEVE_TRANSACTION:
                                //手动下载短信
                                transaction = new RetrieveTransaction(
                                        TransactionService.this, serviceId,
                                        transactionSettings, args.getUri());
                                break;
                            case Transaction.SEND_TRANSACTION:
                                transaction = new SendTransaction(
                                        TransactionService.this, serviceId,
                                        transactionSettings, args.getUri());
                                break;
                            case Transaction.READREC_TRANSACTION:
                                transaction = new ReadRecTransaction(
                                        TransactionService.this, serviceId,
                                        transactionSettings, args.getUri());
                                break;
                            default:
                                Log.w(TAG, "Invalid transaction type: " + serviceId);
                                transaction = null;
                                return;
                        }
                        //copy the subId from TransactionBundle to transaction obj.
                        transaction.setSubId(args.getSubId());
                        //启动线程,进行下载,或发送操作
                        if (!processTransaction(transaction)) {
                            transaction = null;
                            return;
                        }

                        LogTag.debugD("Started processing of incoming message: " + msg);
                    } catch (Exception ex) {
                        Log.w(TAG, "Exception occurred while handling message: " + msg, ex);

                        if (transaction != null) {
                            try {
                                transaction.detach(TransactionService.this);
                                if (mProcessing.contains(transaction)) {
                                    synchronized (mProcessing) {
                                        mProcessing.remove(transaction);
                                    }
                                }
                            } catch (Throwable t) {
                                Log.e(TAG, "Unexpected Throwable.", t);
                            } finally {
                                // Set transaction to null to allow stopping the
                                // transaction service.
                                transaction = null;
                            }
                        }
                    } finally {
                        if (transaction == null) {
                            LogTag.debugD("Transaction was null. Stopping self: " + serviceId);
                            endMmsConnectivity();
                            stopSelfIfIdle(serviceId);
                        }
                    }
                    return;
                case EVENT_HANDLE_NEXT_PENDING_TRANSACTION:
                    processPendingTransaction(transaction, (TransactionSettings) msg.obj);
                    return;
                case EVENT_MMS_CONNECTIVITY_TIMEOUT:
                    removeMessages(EVENT_MMS_CONNECTIVITY_TIMEOUT);
                    mMmsConnecvivityRetryCount++;
                    if (mMmsConnecvivityRetryCount > MMS_CONNECTIVITY_RETRY_TIMES) {
                        Log.d(TAG, "MMS_CONNECTIVITY_TIMEOUT");
                        mMmsConnecvivityRetryCount = 0;
                        return;
                    }

                    if (!mPending.isEmpty()) {
                        try {
                            beginMmsConnectivity(SubscriptionManager.getDefaultDataSubscriptionId());
                        } catch (IOException e) {
                            Log.w(TAG, "Attempt to use of MMS connectivity failed");
                            return;
                        }
                    }
                    return;
                case EVENT_MMS_PDP_ACTIVATION_TIMEOUT:
                    onPDPTimeout((int)msg.obj);
                    return;
                default:
                  
                    return;
            }
        }

看这个方法  

processTransaction(transaction)
  private boolean processTransaction(Transaction transaction) throws IOException {
            // Check if transaction already processing
            synchronized (mProcessing) {
                for (Transaction t : mPending) {
                    if (t.isEquivalent(transaction)) {
                        LogTag.debugD("Transaction already pending: " +
                                transaction.getServiceId());
                        return true;
                    }
                }
                for (Transaction t : mProcessing) {
                    if (t.isEquivalent(transaction)) {
                        LogTag.debugD("Duplicated transaction: " + transaction.getServiceId());
                        return true;
                    }
                }

                int subId = transaction.getSubId();
                int phoneId = SubscriptionManagerWrapper.getPhoneId(subId);
                boolean isRequestNetworkQueued = true;
                LogTag.debugD("processTransaction :subId="+subId
                    + "; phoneId = " + phoneId
                    + "; mPhoneCount = "+ mPhoneCount);
                for (int id =0; id < mPhoneCount; id++) {
                    if ((id != phoneId) && (mMmsNetworkRequest[id] != null)) {
                        isRequestNetworkQueued = false;
                        break;
                    }
                }
                LogTag.debugD("processTransaction :isRequestNetworkQueued="+isRequestNetworkQueued);

                if ((mProcessing.size() > 0) || !isRequestNetworkQueued) {
                    LogTag.debugD("Adding transaction to 'mPending' list: " + transaction);
                    mPending.add(transaction);
                    return true;
                } else {
                    beginMmsConnectivity(subId);

                if (!mIsAvailable[phoneId]) {
                    mPending.add(transaction);
                    LogTag.debugD("processTransaction: connResult=APN_REQUEST_STARTED, " +
                            "defer transaction pending MMS connectivity");
                    if (transaction instanceof SendTransaction) {
                        LogTag.debugD("remove cache while deferring");
                        MmsApp.getApplication().getPduLoaderManager().removePdu(
                                ((SendTransaction) transaction).mSendReqURI);
                    }
                        return true;
                    }

                    LogTag.debugD("Adding transaction to 'mProcessing' list: " + transaction);
                    mProcessing.add(transaction);
                }
            }

            LogTag.debugD("processTransaction: starting transaction " + transaction);

            transaction.attach(TransactionService.this);
            transaction.process();
            return true;
        }
    }

在最后启动了  transaction.process();

开启了RetrieveTransaction.java

RetrieveTransaction是一个线程类



package com.android.mms.transaction;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SqliteWrapper;
import android.net.Uri;
import android.provider.Telephony.Mms;
import android.provider.Telephony.Mms.Inbox;
import android.text.TextUtils;
import android.util.Log;

import com.android.mms.LogTag;
import com.android.mms.MmsConfig;
import com.android.mms.R;
import com.android.mms.ui.MessageUtils;
import com.android.mms.ui.MessagingPreferenceActivity;
import com.android.mms.util.DownloadManager;
import com.android.mms.util.Recycler;
import com.android.mms.widget.MmsWidgetProvider;
import com.google.android.mms.MmsException;
import com.google.android.mms.pdu.AcknowledgeInd;
import com.google.android.mms.pdu.EncodedStringValue;
import com.google.android.mms.pdu.PduComposer;
import com.google.android.mms.pdu.PduHeaders;
import com.google.android.mms.pdu.PduParser;
import com.google.android.mms.pdu.PduPersister;
import com.google.android.mms.pdu.RetrieveConf;

import java.io.IOException;

public class RetrieveTransaction extends Transaction implements Runnable {
    private static final String TAG = LogTag.TAG;
    private static final boolean DEBUG = false;
    private static final boolean LOCAL_LOGV = false;

    private final Uri mUri;
    private final String mContentLocation;
    private boolean mLocked;
    private boolean isCancelMyself;

    static final String[] PROJECTION = new String[] {
        Mms.CONTENT_LOCATION,
        Mms.LOCKED
    };

    // The indexes of the columns which must be consistent with above PROJECTION.
    static final int COLUMN_CONTENT_LOCATION      = 0;
    static final int COLUMN_LOCKED                = 1;

    public RetrieveTransaction(Context context, int serviceId,
            TransactionSettings connectionSettings, String uri)
            throws MmsException {
        super(context, serviceId, connectionSettings);

        if (uri.startsWith("content://")) {
            mUri = Uri.parse(uri); // The Uri of the M-Notification.ind
            mId = mContentLocation = getContentLocation(context, mUri);
            if (LOCAL_LOGV) {
                Log.v(TAG, "X-Mms-Content-Location: " + mContentLocation);
            }
        } else {
            throw new IllegalArgumentException(
                    "Initializing from X-Mms-Content-Location is abandoned!");
        }

        // Attach the transaction to the instance of RetryScheduler.
        attach(RetryScheduler.getInstance(context));
    }

    private String getContentLocation(Context context, Uri uri)
            throws MmsException {
        Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
                            uri, PROJECTION, null, null, null);
        mLocked = false;

        if (cursor != null) {
            try {
                if ((cursor.getCount() == 1) && cursor.moveToFirst()) {
                    // Get the locked flag from the M-Notification.ind so it can be transferred
                    // to the real message after the download.
                    mLocked = cursor.getInt(COLUMN_LOCKED) == 1;
                    return cursor.getString(COLUMN_CONTENT_LOCATION);
                }
            } finally {
                cursor.close();
            }
        }

        throw new MmsException("Cannot get X-Mms-Content-Location from: " + uri);
    }

    /*
     * (non-Javadoc)
     * @see com.android.mms.transaction.Transaction#process()
     */
    @Override
    public void process() {
        new Thread(this, "RetrieveTransaction").start();
    }

    public void run() {
        try {
            DownloadManager downloadManager = DownloadManager.getInstance();
            //Obtain Message Size from M-Notification.ind for original MMS
            int msgSize = downloadManager.getMessageSize(mUri);

            // Change the downloading state of the M-Notification.ind.
            downloadManager.markState(mUri, DownloadManager.STATE_DOWNLOADING);

            if (isCancelMyself) {
                DownloadManager.getInstance().markState(mUri,
                        DownloadManager.STATE_TRANSIENT_FAILURE);
                return;
            }
            // Send GET request to MMSC and retrieve the response data.   下载彩信
            byte[] resp= getPdu(mContentLocation,mUri);

            if (isCancelMyself) {
                DownloadManager.getInstance().markState(mUri,
                        DownloadManager.STATE_TRANSIENT_FAILURE);
                return;
            }

            // Parse M-Retrieve.conf  从网上获取到的byte数字  给PduParser进行解析
            RetrieveConf retrieveConf = (RetrieveConf) new PduParser(resp,PduParserUtil.shouldParseContentDisposition()).parse();
            if (null == retrieveConf) {
                throw new MmsException("Invalid M-Retrieve.conf PDU.");
            }

            Uri msgUri = null;
            if (isDuplicateMessage(mContext, retrieveConf, mContentLocation)) {
                // Mark this transaction as failed to prevent duplicate
                // notification to user.
                if (Log.isLoggable(LogTag.TRANSACTION, Log.DEBUG)) {
                    Log.v(TAG, "RetrieveTransaction DuplicateMessage !!");
                }
                mTransactionState.setState(TransactionState.FAILED);
                mTransactionState.setContentUri(mUri);
            } else {
                // Store M-Retrieve.conf into Inbox//将数据给PduPersister进行解析   插入到Part表中  在retrieveConf中的byte数组包括全部东西,有彩信,文本。。。
                PduPersister persister = PduPersister.getPduPersister(mContext);
                msgUri = persister.persist(retrieveConf, Inbox.CONTENT_URI, true,
                        MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext), null);

                // Use local time instead of PDU time
                ContentValues values = new ContentValues(3);
                values.put(Mms.DATE, System.currentTimeMillis() / 1000L);
                Cursor c = mContext.getContentResolver().query(mUri,
                        null, null, null, null);
                if (c != null) {
                    try {
                        if (c.moveToFirst()) {
                            int subId = c.getInt(c.getColumnIndex(Mms.SUBSCRIPTION_ID));
                            Log.d(TAG, "RetrieveTransaction: subId value is " + subId);
                            values.put(Mms.SUBSCRIPTION_ID, subId);
                        }
                    } finally {
                        c.close();
                    }
                }
                // Update Message Size for Original MMS.
                values.put(Mms.MESSAGE_SIZE, msgSize);
                SqliteWrapper.update(mContext, mContext.getContentResolver(),
                        msgUri, values, null, null);

                // The M-Retrieve.conf has been successfully downloaded.
                mTransactionState.setState(TransactionState.SUCCESS);
                mTransactionState.setContentUri(msgUri);
                // Remember the location the message was downloaded from.
                // Since it's not critical, it won't fail the transaction.
                // Copy over the locked flag from the M-Notification.ind in case
                // the user locked the message before activating the download.
                updateContentLocation(mContext, msgUri, mContentLocation, mLocked);
            }

            // Delete the corresponding M-Notification.ind.
            SqliteWrapper.delete(mContext, mContext.getContentResolver(),
                                 mUri, null, null);

            if (msgUri != null) {
                // Have to delete messages over limit *after* the delete above. Otherwise,
                // it would be counted as part of the total.
                Recycler.getMmsRecycler().deleteOldMessagesInSameThreadAsMessage(mContext, msgUri);
                MmsWidgetProvider.notifyDatasetChanged(mContext);
            }

            // Send ACK to the Proxy-Relay to indicate we have fetched the
            // MM successfully.
            // Don't mark the transaction as failed if we failed to send it.
            sendAcknowledgeInd(retrieveConf);
        } catch (Throwable t) {
            Log.e(TAG, Log.getStackTraceString(t));
        } finally {
            if (mTransactionState.getState() != TransactionState.SUCCESS) {
                if (isCancelMyself) {
                    mTransactionState.setState(TransactionState.CANCELED);
                } else {
                    mTransactionState.setState(TransactionState.FAILED);
                }
                mTransactionState.setContentUri(mUri);
                Log.e(TAG, "Retrieval failed.");
            }
            notifyObservers();
        }
    }

    private static boolean isDuplicateMessage(Context context, RetrieveConf rc, String location) {
        byte[] rawMessageId = rc.getMessageId();
        if (rawMessageId != null) {
            String messageId = new String(rawMessageId);
            String selection = "(" + Mms.MESSAGE_ID + " = ? AND "
                                   + Mms.CONTENT_LOCATION + " = ? AND "
                                   + Mms.MESSAGE_TYPE + " = ?)";
            String[] selectionArgs = new String[] { messageId, location,
                    String.valueOf(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) };

            Cursor cursor = SqliteWrapper.query(
                    context, context.getContentResolver(),
                    Mms.CONTENT_URI, new String[] { Mms._ID, Mms.SUBJECT, Mms.SUBJECT_CHARSET },
                    selection, selectionArgs, null);

            if (cursor != null) {
                try {
                    if (cursor.getCount() > 0) {
                        // A message with identical message ID and type found.
                        // Do some additional checks to be sure it's a duplicate.
                        return isDuplicateMessageExtra(cursor, rc);
                    }
                } finally {
                    cursor.close();
                }
            }
        }
        return false;
    }

    private static boolean isDuplicateMessageExtra(Cursor cursor, RetrieveConf rc) {
        // Compare message subjects, taking encoding into account
        EncodedStringValue encodedSubjectReceived = null;
        EncodedStringValue encodedSubjectStored = null;
        String subjectReceived = null;
        String subjectStored = null;
        String subject = null;

        encodedSubjectReceived = rc.getSubject();
        if (encodedSubjectReceived != null) {
            subjectReceived = encodedSubjectReceived.getString();
        }

        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
            int subjectIdx = cursor.getColumnIndex(Mms.SUBJECT);
            int charsetIdx = cursor.getColumnIndex(Mms.SUBJECT_CHARSET);
            subject = cursor.getString(subjectIdx);
            int charset = cursor.getInt(charsetIdx);
            if (subject != null) {
                encodedSubjectStored = new EncodedStringValue(charset, PduPersister
                        .getBytes(subject));
            }
            if (encodedSubjectStored == null && encodedSubjectReceived == null) {
                // Both encoded subjects are null - return true
                return true;
            } else if (encodedSubjectStored != null && encodedSubjectReceived != null) {
                subjectStored = encodedSubjectStored.getString();
                if (!TextUtils.isEmpty(subjectStored) && !TextUtils.isEmpty(subjectReceived)) {
                    // Both decoded subjects are non-empty - compare them
                    return subjectStored.equals(subjectReceived);
                } else if (TextUtils.isEmpty(subjectStored) && TextUtils.isEmpty(subjectReceived)) {
                    // Both decoded subjects are "" - return true
                    return true;
                }
            }
        }

        return false;
    }

    private void sendAcknowledgeInd(RetrieveConf rc) throws MmsException, IOException {
        // Send M-Acknowledge.ind to MMSC if required.
        // If the Transaction-ID isn't set in the M-Retrieve.conf, it means
        // the MMS proxy-relay doesn't require an ACK.
        byte[] tranId = rc.getTransactionId();
        if (tranId != null) {
            // Create M-Acknowledge.ind
            AcknowledgeInd acknowledgeInd = new AcknowledgeInd(
                    PduHeaders.CURRENT_MMS_VERSION, tranId);

            // insert the 'from' address per spec
            String lineNumber = MessageUtils.getLocalNumber();
            acknowledgeInd.setFrom(new EncodedStringValue(lineNumber));
            if (false){
                // Pack M-Acknowledge.ind and send it
                if(MmsConfig.getNotifyWapMMSC()) {
                    sendPdu(new PduComposer(mContext, acknowledgeInd).make(), mContentLocation);
                } else {
                    sendPdu(new PduComposer(mContext, acknowledgeInd).make());
                }
            }
        }
    }

    private static void updateContentLocation(Context context, Uri uri,
                                              String contentLocation,
                                              boolean locked) {
        ContentValues values = new ContentValues(2);
        values.put(Mms.CONTENT_LOCATION, contentLocation);
        values.put(Mms.LOCKED, locked);     // preserve the state of the M-Notification.ind lock.
        SqliteWrapper.update(context, context.getContentResolver(),
                             uri, values, null, null);
    }

    @Override
    public void abort() {
        Log.d(TAG, "markFailed = " + this);
        mTransactionState.setState(TransactionState.FAILED);
        mTransactionState.setContentUri(mUri);
        mFailReason = FAIL_REASON_CAN_NOT_SETUP_DATA_CALL;
        notifyObservers();
    }

    @Override
    public int getType() {
        return RETRIEVE_TRANSACTION;
    }

    @Override
    public void cancelTransaction(Uri uri) {
        if (mUri.equals(uri)) {
            isCancelMyself = true;
        }
    }
}

通过Http进行下载获取到Byte数字

// Send GET request to MMSC and retrieve the response data.   下载彩信
byte[] resp= getPdu(mContentLocation,mUri);

getPdu 里面的操作是网络操作,主要是到MMSC服务器中下载数据下载后返回的是一个Byte数组

// Parse M-Retrieve.conf  从网上获取到的byte数字  给PduParser进行解析
RetrieveConf retrieveConf = (RetrieveConf) new PduParser(resp,PduParserUtil.shouldParseContentDisposition()).parse();
// Store M-Retrieve.conf into Inbox//将数据给PduPersister进行解析   插入到Part表中  在retrieveConf中的byte数组包括全部东西,有彩信,文本。。。
PduPersister persister = PduPersister.getPduPersister(mContext);
msgUri = persister.persist(retrieveConf, Inbox.CONTENT_URI, true,
        MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext), null);

这一步part表就有数据了彩信下载成功了

猜你喜欢

转载自blog.csdn.net/qq_35427437/article/details/93047580