Android M MO SMS/MMS FLOW

Android M MO SMS/MMS FLOW
Android发送短彩的接口统一定义在SmsManager, 目录frameworks/opt/telephony/src/java/android/telephony/SmsManager.java
然后由短信APP来调用发送短彩。
1.SMS,短信的接口有很多个,具体都是怎么用的,可以查看注释。下面copy两个注释比较长的接口来介绍
Interface1
    /**
     * Send a text based SMS.
     *
     *

Note: Using this method requires that your app has the
     * {@link android.Manifest.permission#SEND_SMS} permission.


     *
     *

Note: Beginning with Android 4.4 (API level 19), if
     * and only if an app is not selected as the default SMS app, the system automatically
     * writes messages sent using this method to the SMS Provider (the default SMS app is always
     * responsible for writing its sent messages to the SMS Provider). For information about
     * how to behave as the default SMS app, see {@link android.provider.Telephony}.


     *
     *
     * @param destinationAddress the address to send the message to
     * @param scAddress is the service center address or null to use
     *  the current default SMSC
     * @param text the body of the message to send
     * @param sentIntent if not NULL this PendingIntent is
     *  broadcast when the message is successfully sent, or failed.
     *  The result code will be Activity.RESULT_OK for success,
     *  or one of these errors:

     *  RESULT_ERROR_GENERIC_FAILURE

     *  RESULT_ERROR_RADIO_OFF

     *  RESULT_ERROR_NULL_PDU

     *  For RESULT_ERROR_GENERIC_FAILURE the sentIntent may include
     *  the extra "errorCode" containing a radio technology specific value,
     *  generally only useful for troubleshooting.

     *  The per-application based SMS control checks sentIntent. If sentIntent
     *  is NULL the caller will be checked against all unknown applications,
     *  which cause smaller number of SMS to be sent in checking period.
     * @param deliveryIntent if not NULL this PendingIntent is
     *  broadcast when the message is delivered to the recipient.  The
     *  raw pdu of the status report is in the extended data ("pdu").
     *
     * @throws IllegalArgumentException if destinationAddress or text are empty
     */
    public void sendTextMessage(
            String destinationAddress, String scAddress, String text,
            PendingIntent sentIntent, PendingIntent deliveryIntent) {
        sendTextMessageInternal(destinationAddress, scAddress, text,
            sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/);
    }


Interface2
    /**
     * Send a multi-part text based SMS.  The callee should have already
     * divided the message into correctly sized parts by calling
     * divideMessage.
     *
     *

Note: Using this method requires that your app has the
     * {@link android.Manifest.permission#SEND_SMS} permission.


     *
     *

Note: Beginning with Android 4.4 (API level 19), if
     * and only if an app is not selected as the default SMS app, the system automatically
     * writes messages sent using this method to the SMS Provider (the default SMS app is always
     * responsible for writing its sent messages to the SMS Provider). For information about
     * how to behave as the default SMS app, see {@link android.provider.Telephony}.


     *
     * @param destinationAddress the address to send the message to
     * @param scAddress is the service center address or null to use
     *   the current default SMSC
     * @param parts an ArrayList of strings that, in order,
     *   comprise the original message
     * @param sentIntents if not null, an ArrayList of
     *   PendingIntents (one for each message part) that is
     *   broadcast when the corresponding message part has been sent.
     *   The result code will be Activity.RESULT_OK for success,
     *   or one of these errors:

     *   RESULT_ERROR_GENERIC_FAILURE

     *   RESULT_ERROR_RADIO_OFF

     *   RESULT_ERROR_NULL_PDU

     *   For RESULT_ERROR_GENERIC_FAILURE each sentIntent may include
     *   the extra "errorCode" containing a radio technology specific value,
     *   generally only useful for troubleshooting.

     *   The per-application based SMS control checks sentIntent. If sentIntent
     *   is NULL the caller will be checked against all unknown applications,
     *   which cause smaller number of SMS to be sent in checking period.
     * @param deliveryIntents if not null, an ArrayList of
     *   PendingIntents (one for each message part) that is
     *   broadcast when the corresponding message part has been delivered
     *   to the recipient.  The raw pdu of the status report is in the
     *   extended data ("pdu").
     *
     * @throws IllegalArgumentException if destinationAddress or data are empty
     */
    public void sendMultipartTextMessage(
            String destinationAddress, String scAddress, ArrayList parts,
            ArrayList sentIntents, ArrayList deliveryIntents) {
        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts,
              sentIntents, deliveryIntents, true /* persistMessageForCarrierApp*/);
    }
Interface1和Interface2应该是分别对应短短信和长短信(一条装不下,需要分成几个部分)。注释写的很清晰了,@param destinationAddress打印出的话,可以看到就是要发送的手机号码。
@param scAddress 是SMSC 短信中心,如果没有看过短信app的代码的话可以对@param sentIntents 和@param deliveryIntents比较迷惑。以后完善的时候在介绍这两个参数吧。
反正SmsManager提供的短信发送接口并不会对短信做什么处理,只起到一个连接的作用,调用SmsManager的短信发送接口后会通过IPC来拿到短信服务发远程实例,代码大概是这个样子
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
这个短信服务的具体实现又是谁呢? -> UiccSmsController
    public void sendTextForSubscriber(int subId, String callingPackage, String destAddr,
            String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
            boolean persistMessageForNonDefaultSmsApp) {
        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
        if (iccSmsIntMgr != null) {
            iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,
                    deliveryIntent, persistMessageForNonDefaultSmsApp);
        } else {
            Rlog.e(LOG_TAG,"sendTextForSubscriber iccSmsIntMgr is null for" +
                          " Subscription: " + subId);
            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
        }
    }
看样子UiccSmsController也不负责处理,那么看一下IccSmsInterfaceManager
    /**
     * Send a text based SMS.
     *
     * @param destAddr the address to send the message to
     * @param scAddr is the service center address or null to use
     *  the current default SMSC
     * @param text the body of the message to send
     * @param sentIntent if not NULL this PendingIntent is
     *  broadcast when the message is successfully sent, or failed.
     *  The result code will be Activity.RESULT_OK for success,
     *  or one of these errors:

     *  RESULT_ERROR_GENERIC_FAILURE

     *  RESULT_ERROR_RADIO_OFF

     *  RESULT_ERROR_NULL_PDU

     *  For RESULT_ERROR_GENERIC_FAILURE the sentIntent may include
     *  the extra "errorCode" containing a radio technology specific value,
     *  generally only useful for troubleshooting.

     *  The per-application based SMS control checks sentIntent. If sentIntent
     *  is NULL the caller will be checked against all unknown applications,
     *  which cause smaller number of SMS to be sent in checking period.
     * @param deliveryIntent if not NULL this PendingIntent is
     *  broadcast when the message is delivered to the recipient.  The
     *  raw pdu of the status report is in the extended data ("pdu").
     */


    private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
            boolean persistMessageForNonDefaultSmsApp) {
        if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
            log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
                " text='"+ text + "' sentIntent=" +
                sentIntent + " deliveryIntent=" + deliveryIntent);
        }
        if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
                callingPackage) != AppOpsManager.MODE_ALLOWED) {
            return;
        }
        if (!persistMessageForNonDefaultSmsApp) {
            // Only allow carrier app to skip auto message persistence.
            enforceCarrierPrivilege();
        }
        destAddr = filterDestAddress(destAddr);
        mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
    }
也没做啥事情,destAddr = filterDestAddress(destAddr); 接收者的电话号码到是被过滤了一下,这个过滤规则和运营商有关系,没有细看~影响不大,貌似中国的过滤完是不变的。
mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
mDispatcher 是ImsSmsDispatcher
    protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
            boolean persistMessage) {
        Rlog.d(TAG, "sendText");
        if (isCdmaMo()) {
            mCdmaDispatcher.sendText(destAddr, scAddr,
                    text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
        } else {
            mGsmDispatcher.sendText(destAddr, scAddr,
                    text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
        }
    }
mGsmDispatcher 是GsmSmsDispatcher
    protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
            boolean persistMessage) {
        SmsMessage.SubmitPdu pdu = null;
        if (FEATURE_VALIDITY_PERIOD_ID == 0 || !mContext.getResources().getBoolean(
                FEATURE_VALIDITY_PERIOD_ID)) {
            pdu = SmsMessage.getSubmitPdu(
                    scAddr, destAddr, text, (deliveryIntent != null));
        } else {
            SmsManager manager = SmsManager.getSmsManagerForSubscriptionId(getSubId());
            pdu = (SmsMessage.SubmitPdu)mSmsInterface.getSubmitPdu(
                    scAddr, destAddr, text, (deliveryIntent != null),
                    manager.getSmsValidityPeriod());
        }
        if (pdu != null) {
            HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
                    messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/,
                    persistMessage);


            String carrierPackage = getCarrierAppPackageName();
            if (carrierPackage != null) {
                Rlog.d(TAG, "Found carrier package.");
                TextSmsSender smsSender = new TextSmsSender(tracker);
                smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
            } else {
                Rlog.v(TAG, "No carrier package.");
                sendRawPdu(tracker);
            }
        } else {
            Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
        }
    }
第一步先生成pdu,然后把destAddr,scAddr,text,pdu集成到一个map里。
HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
第二步构造tracker,SmsTracker tracker = ...
carrierPackage没见过,所以一般都走这里
Rlog.v(TAG, "No carrier package.");
sendRawPdu(tracker); //检查pdu,package name,appinfo...
->sendSms(tracker);
-->        Rlog.d(TAG, "sendSms: "
                + " isIms()=" + isIms()
                + " mRetryCount=" + tracker.mRetryCount
                + " mImsRetry=" + tracker.mImsRetry
                + " mMessageRef=" + tracker.mMessageRef
                + " SS=" + mPhone.getServiceState().getState());
//这算一个关键的log吧~
       sendSmsByPstn(tracker);
--->这里会根据是否注册上ims和其他相关条件来调用不用的方法,
Ims:
mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
                    IccUtils.bytesToHexString(pdu), tracker.mImsRetry,
                    tracker.mMessageRef, reply);
非Ims:
mCi.sendSMS(IccUtils.bytesToHexString(smsc),
                        IccUtils.bytesToHexString(pdu), reply);
sendSmsByPstn这个方法内还有一个回调,Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
看一下放松成功后的处理,在SmsDispather
handleSendComplete((AsyncResult) msg.obj);
处理根据结果是否携带异常分为成功和失败两种,
success:
tracker.onSent(mContext);
fail:
tracker.onFailed
or sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
看一下成功的处理
SmsTracker
persistOrUpdateMessage(context, messageType, 0/*errorCode*/);//貌似要保存信息
        /**
         * Persist or update an SMS depending on if we send a new message or a stored message
         *
         * @param context
         * @param messageType The message folder for this SMS, FAILED or SENT
         * @param errorCode The current error code for this SMS or SMS part
         */
        private void persistOrUpdateMessage(Context context, int messageType, int errorCode) {
            if (mMessageUri != null) {
                updateMessageState(context, messageType, errorCode);//已经存过了,更新一下状态?
            } else {
                mMessageUri = persistSentMessageIfRequired(context, messageType, errorCode);//存?
            }
        }
mSentIntent.send(context, Activity.RESULT_OK, fillIn);//用来通知app??

你可能感兴趣的:(Telephony)