`

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);
        }
    }

 

 

  • 大小: 38.4 KB
  • 大小: 106.5 KB
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    短信发送流程图形剖析(含编码)Android平台整理.pdf

    短信发送流程图形剖析(含编码)Android平台整理.pdf

    Android开发实验---通讯录.docx

    实验项目名称 通讯录 实验目的与要求: 目的:练习掌握 Android 软件开发基本编程技术、Android 系统 SQLite 数据库的使用、通话、短信的使用等,设计制作一 Android 通讯录软件。 要求: (1)每位同学独立设计...

    android开发实例大全_王东华

    本书以Android应用程序的开发为主题,并结合真实的案例向读者详细介绍了Android的基本组件的使用及应用程序开发的整个流程。本书的讲述由浅入深,实例全面并典型,几乎囊括了所有和Android应用相关的项目。全书分为...

    安卓课程设计-手机通讯录系统全解.doc

    7 3.1.5 还原与备份功能 7 3.2 系统界面设计 8 4 系统编码实现 14 前 言 随着移动通信与Internet向移动终端的普及,网络和用户对移动终端的要求越来越高 ,而Symbian,Windows Mobile,PalmOS等手机平台过于封闭,不...

    微信公众平台应用开发:方法、技巧与案例.(机械工业.柳峰)

    他还是一位资深的Java软件开发工程师和Android/iOS移动应用开发工程师,活跃于CocoaChina、开源中国、CSDN等社区,CSDN博客专家,在CSDN博客撰写了系列微信公众平台二次开发的教程,深受欢迎并被广泛传播,也因此...

    JAVA上百实例源码以及开源项目

    此时此景,笔者只专注Android、Iphone等移动平台开发,看着这些源码心中有万分感慨,写此文章纪念那时那景! Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这...

    java开源包8

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    JAVA上百实例源码以及开源项目源代码

    此时此景,笔者只专注Android、Iphone等移动平台开发,看着这些源码心中有万分感慨,写此文章纪念那时那景! Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这...

    java开源包1

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包11

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包2

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包3

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包6

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包5

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包10

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包4

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包7

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包9

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

    java开源包101

    Java发送短信包 LemonSMS LemonSMS 这个Java库可以让开发者在应用程序中集成使用GSM调制解调器或兼容电话来发送SMS消息。 远程桌面 Java Remote Desktop.tar Java Remote Desktop 是一个Java 的远程桌面软件,支持...

Global site tag (gtag.js) - Google Analytics