Android平台 短信发送流程剖析(含编码)

Android平台 短信发送流程剖析(含编码)

本文对Android平台短信发送流程进行了走读和剖析,特别是编码部分,今天将流程整理出来,以便平时参考,也希望对大家有用!!!

先上图,下面2个图是用PPT画的,这里截图附上来:

                                           流程图1:


 

 

 

 

                                                            流程图2:

  

发送流程编码解析:

从上图中的GsmSMSDispatchersendText开始分析

 

//GsmSMSDispatcher.java
/** {@inheritDoc} */
@Override
// ①入口
    protected void sendText(String destAddr, String scAddr, String text,
            PendingIntent sentIntent, PendingIntent deliveryIntent) {
        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu (  //转②分析
                scAddr, destAddr, text, (deliveryIntent != null));

        HashMap map =  SmsTrackerMapFactory(destAddr, scAddr, text, pdu);
        SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent,
                RadioTechnologyFamily.RADIO_TECH_3GPP);
        sendRawPdu(tracker); //转I分析
}

②分析:
//SmsMessage.java
  public static SubmitPdu getSubmitPdu(String scAddress,
            String destinationAddress, String message,
            boolean statusReportRequested) {
return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null); //转③分析
    }

③分析
//SmsMessage.java
 public static SubmitPdu getSubmitPdu(String scAddress,
            String destinationAddress, String message,
            boolean statusReportRequested, byte[] header) {
 return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,ENCODING_UNKNOWN/*默认编码方式*/); //转④分析
    }

④分析 编码核心函数
//SmsMessage.java
public static SubmitPdu getSubmitPdu(String scAddress,
            String destinationAddress, String message,
            boolean statusReportRequested, byte[] header, int encoding) {
          // 3
        // Perform null parameter checks.
        if (message == null || destinationAddress == null) {
            return null;
        }

        SubmitPdu ret = new SubmitPdu();
        // MTI = SMS-SUBMIT, UDHI = header != null
        byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00));
//MTI :bit 0 和 bit1  UDHI:bit6 
          // 0x01 = 0000 0001  0x40 = 0010 0000
        ByteArrayOutputStream bo = getSubmitPduHead(
                scAddress, destinationAddress, mtiByte,
                statusReportRequested, ret); //转⑤分析
        // User Data (and length)  //TP-DCS 和TP-UDL
        byte[] userData;
        if (encoding == ENCODING_UNKNOWN) { 
            // First, try encoding it with the GSM alphabet
            encoding = ENCODING_7BIT; //默认先采用ENCODING_7BIT编码模式
        }
        try {
            if (encoding == ENCODING_7BIT) {
         userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header);
//采用ENCODING_7BIT进行编码,若出现编码异常,函数会抛出异常:EncodeException,转至⑥处。编码成功转至⑦。stringToGsm7BitPackedWithHeader分析转⑧处
            } else { //assume UCS-2
                try {
                    userData = encodeUCS2(message, header);
                } catch(UnsupportedEncodingException uex) {
                    Log.e(LOG_TAG,
                            "Implausible UnsupportedEncodingException ",
                            uex);
                    return null;
                }
            }
        } catch (EncodeException ex) { //⑥ 7bit编码模式失败后,就采用UCS-2进行编码
            // Encoding to the 7-bit alphabet failed. Let's see if we can
            // send it as a UCS-2 encoded message
            try {
                userData = encodeUCS2(message, header);
                encoding = ENCODING_16BIT;
            } catch(UnsupportedEncodingException uex) {
                Log.e(LOG_TAG,
                        "Implausible UnsupportedEncodingException ",
                        uex);
                return null;
            }
        }

        if (encoding == ENCODING_7BIT) {  //⑦ 7bit编码成功
            if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
                // Message too long
                return null;
            }
            // TP-Data-Coding-Scheme
            // Default encoding, uncompressed
            // To test writing messages to the SIM card, change this value 0x00
            // to 0x12, which means "bits 1 and 0 contain message class, and the
            // class is 2". Note that this takes effect for the sender. In other
            // words, messages sent by the phone with this change will end up on
            // the receiver's SIM card. You can then send messages to yourself
            // (on a phone with this change) and they'll end up on the SIM card.
//0x12 = 0001 0010 未压缩,class2,存储到SIM卡
//0x00 = 0000 0000 未压缩,class0,GSM7bit编码
            bo.write(0x00);
        } else { // assume UCS-2
            if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
                // Message too long
                return null;
            }
            // TP-Data-Coding-Scheme
            // Class 3, UCS-2 encoding, uncompressed
            bo.write(0x0b);  //0x0b = 0000 1011 未压缩,class3,UCS-2编码
        }

        // (no TP-Validity-Period)
        bo.write(userData, 0, userData.length);
        ret.encodedMessage = bo.toByteArray();
        return ret;
    }

⑤分析
//SmsMessage.java
  private static ByteArrayOutputStream getSubmitPduHead(
            String scAddress, String destinationAddress, byte mtiByte,
            boolean statusReportRequested, SubmitPdu ret) 
//scAddress为短信中心号码,destinationAddress为目标地址号码
//mtiByte为MTI和UDHI 编码数据,见上面分析,statusReportRequested为状态报告 //ret 下面会写入数据到ret
{
        ByteArrayOutputStream bo = new ByteArrayOutputStream(
                MAX_USER_DATA_BYTES + 40);

        // SMSC address with length octet, or 0
        if (scAddress == null) {
            ret.encodedScAddress = null;
        } else {
            ret.encodedScAddress = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
                    scAddress);
        }
        // TP-Message-Type-Indicator (and friends)
        if (statusReportRequested) {
            // Set TP-Status-Report-Request bit.    //TP-SRR bit 5 ,0x20 = 0010 0000
            mtiByte |= 0x20;
            if (Config.LOGD) Log.d(LOG_TAG, "SMS status report requested");
        }
        bo.write(mtiByte);

        // space for TP-Message-Reference   //TP-MR
        bo.write(0);

        byte[] daBytes;

        daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);

        // destination address length in BCD digits, ignoring TON byte and pad
        // TODO Should be better.
        bo.write((daBytes.length - 1) * 2
                - ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));

        // destination address
        bo.write(daBytes, 0, daBytes.length);

        // TP-Protocol-Identifier   //TP--PID
        bo.write(0);
        return bo;
    }

⑧分析
//GsmAlphabet.java
  public static byte[] stringToGsm7BitPackedWithHeader(String data, byte[] header)
            throws EncodeException { //这里传进来的head为null

        if (header == null || header.length == 0) {
            return stringToGsm7BitPacked(data);  //转⑨分析
        }

        int headerBits = (header.length + 1) * 8;
        int headerSeptets = (headerBits + 6) / 7;

        byte[] ret = stringToGsm7BitPacked(data, headerSeptets, true);

        // Paste in the header
        ret[1] = (byte)header.length;
        System.arraycopy(header, 0, ret, 2, header.length);
        return ret;
    }

⑨分析
//GsmAlphabet.java
  public static byte[] stringToGsm7BitPacked(String data)
            throws EncodeException {
        return stringToGsm7BitPacked(data, 0, true);  //转⑩分析
}

⑩分析
//GsmAlphabet.java
public static byte[] stringToGsm7BitPacked(String data, int startingSeptetOffset,
            boolean throwException) throws EncodeException {
        int dataLen = data.length();
        int septetCount = countGsmSeptets(data, throwException) + startingSeptetOffset;
// 当传入的字符串data中含有charToGsm, charToGsmExtended中没有的字符时(例如汉字),该函数会抛出异常,这样在调用处⑥会捕获该异常。然后会采用UCS-2方式进行编码。

        if (septetCount > 255) {
            throw new EncodeException("Payload cannot exceed 255 septets");
        }
        int byteCount = ((septetCount * 7) + 7) / 8;
        byte[] ret = new byte[byteCount + 1];  // Include space for one byte length prefix.
        for (int i = 0, septets = startingSeptetOffset, bitOffset = startingSeptetOffset * 7;
                 i < dataLen && septets < septetCount;
                 i++, bitOffset += 7) {
            char c = data.charAt(i);
            int v = GsmAlphabet.charToGsm(c, throwException);
            if (v == GSM_EXTENDED_ESCAPE) {
                v = GsmAlphabet.charToGsmExtended(c);  // Lookup the extended char.
                packSmsChar(ret, bitOffset, GSM_EXTENDED_ESCAPE);
                bitOffset += 7;
                septets++;
            }
            packSmsChar(ret, bitOffset, v);
            septets++;
        }
        ret[0] = (byte) (septetCount);  // Validated by check above.
        return ret;
}

I分析:
//SMSDispatcher.java
protected void sendRawPdu(SmsTracker tracker) {
        HashMap map = tracker.mData;
        byte pdu[] = (byte[]) map.get("pdu");

        PendingIntent sentIntent = tracker.mSentIntent;
        if (mSmsSendDisabled) {
            if (sentIntent != null) {
                try {
                    sentIntent.send(RESULT_ERROR_NO_SERVICE);
                } catch (CanceledException ex) {}
            }
            Log.d(TAG, "Device does not support sending sms.");
            return;
        }

        if (pdu == null) {
            if (sentIntent != null) {
                try {
                    sentIntent.send(RESULT_ERROR_NULL_PDU);
                } catch (CanceledException ex) {}
            }
            return;
        }

        int ss = mPhone.getServiceState().getState();

        // if IMS not registered on data and voice is not available...
        if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
            handleNotInService(ss, tracker);
        } else {
            String appName = getAppNameByIntent(sentIntent);
            if (mCounter.check(appName, SINGLE_PART_SMS)) {
                sendSms(tracker); // 转II分析
            } else {
                sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
            }
        }
    }

II分析:
//GsmSMSDispatcher.java
  /** {@inheritDoc} */
    @Override
    protected void sendSms(SmsTracker tracker) {
        HashMap<String, Object> map = tracker.mData;

        byte smsc[] = (byte[]) map.get("smsc");
        byte pdu[] = (byte[]) map.get("pdu");

        Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);

        if (tracker.mRetryCount > 0 || !isIms()) {
            // this is retry, use old method
            mCm.sendSMS(IccUtils.bytesToHexString(smsc),
                    IccUtils.bytesToHexString(pdu), reply);
        } else {
            mCm.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
                    IccUtils.bytesToHexString(pdu), reply);
        }
    }

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值