for cdma concatenated (long) messages, replace ascii7bit with gsm7bit encoding
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 9395d66..890f930 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -22,7 +22,6 @@
 import android.text.TextUtils;
 
 import com.android.internal.telephony.EncodeException;
-import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.ISms;
 import com.android.internal.telephony.IccConstants;
 import com.android.internal.telephony.SmsRawData;
@@ -31,14 +30,12 @@
 import java.util.Arrays;
 import java.util.List;
 
-import static android.telephony.SmsMessage.ENCODING_7BIT;
-import static android.telephony.SmsMessage.ENCODING_8BIT;
-import static android.telephony.SmsMessage.ENCODING_16BIT;
-import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
-import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
-import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
-import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
-import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER;
+/*
+ * TODO(code review): Curious question... Why are a lot of these
+ * methods not declared as static, since they do not seem to require
+ * any local object state?  Assumedly this cannot be changed without
+ * interfering with the API...
+ */
 
 /**
  * Manages SMS operations such as sending data, text, and pdu SMS messages.
@@ -88,7 +85,7 @@
     }
 
     /**
-     * Divide a text message into several messages, none bigger than
+     * Divide a message text into several fragments, none bigger than
      * the maximum SMS message size.
      *
      * @param text the original message.  Must not be null.
@@ -96,40 +93,7 @@
      *   comprise the original message
      */
     public ArrayList<String> divideMessage(String text) {
-        int size = text.length();
-        int[] params = SmsMessage.calculateLength(text, false);
-            /* SmsMessage.calculateLength returns an int[4] with:
-             *   int[0] being the number of SMS's required,
-             *   int[1] the number of code units used,
-             *   int[2] is the number of code units remaining until the next message.
-             *   int[3] is the encoding type that should be used for the message.
-             */
-        int messageCount = params[0];
-        int encodingType = params[3];
-        ArrayList<String> result = new ArrayList<String>(messageCount);
-
-        int start = 0;
-        int limit;
-
-        if (messageCount > 1) {
-            limit = (encodingType == ENCODING_7BIT)?
-                MAX_USER_DATA_SEPTETS_WITH_HEADER: MAX_USER_DATA_BYTES_WITH_HEADER;
-        } else {
-            limit = (encodingType == ENCODING_7BIT)?
-                MAX_USER_DATA_SEPTETS: MAX_USER_DATA_BYTES;
-        }
-
-        try {
-            while (start < size) {
-                int end = GsmAlphabet.findLimitIndex(text, start, limit, encodingType);
-                result.add(text.substring(start, end));
-                start = end;
-            }
-        }
-        catch (EncodeException e) {
-            // ignore it.
-        }
-        return result;
+        return SmsMessage.fragmentText(text);
     }
 
     /**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index b60da5a..775b0343 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -17,12 +17,17 @@
 package android.telephony;
 
 import android.os.Parcel;
+import android.util.Log;
 
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.EncodeException;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
+
+import java.lang.Math;
+import java.util.ArrayList;
 
 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
 
@@ -44,19 +49,41 @@
         UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
     }
 
-    /** Unknown encoding scheme (see TS 23.038) */
+    /**
+     * TODO(cleanup): given that we now have more than one possible
+     * 7bit encoding, this result starts to look rather vague and
+     * maybe confusing...  If this is just an indication of code unit
+     * size, maybe that is no problem.  Otherwise, should we try to
+     * create an aggregate collection of GSM and CDMA encodings?  CDMA
+     * contains a superset of the encodings we use (it does not
+     * support 8-bit GSM, but we also do not use that encoding
+     * currently)...  We could get rid of these and directly reference
+     * the CDMA encoding definitions...
+     */
+
+    /** User data text encoding code unit size */
     public static final int ENCODING_UNKNOWN = 0;
-    /** 7-bit encoding scheme (see TS 23.038) */
     public static final int ENCODING_7BIT = 1;
-    /** 8-bit encoding scheme (see TS 23.038) */
     public static final int ENCODING_8BIT = 2;
-    /** 16-bit encoding scheme (see TS 23.038) */
     public static final int ENCODING_16BIT = 3;
 
     /** The maximum number of payload bytes per message */
     public static final int MAX_USER_DATA_BYTES = 140;
 
     /**
+     * TODO(cleanup): It would be more flexible and less fragile to
+     * rewrite this (meaning get rid of the following constant) such
+     * that an actual UDH is taken into consideration (meaning its
+     * length is measured), allowing for messages that actually
+     * contain other UDH fields...  Hence it is actually a shame to
+     * extend the API with this constant.  If necessary, maybe define
+     * the size of such a header and let the math for calculating
+     * max_octets/septets be done elsewhere.  And, while I am griping,
+     * if we use the word septet, we should use the word octet in
+     * corresponding places, not byte...
+     */
+
+    /**
      * The maximum number of payload bytes per message if a user data header
      * is present.  This assumes the header only contains the
      * CONCATENATED_8_BIT_REFERENCE element.
@@ -222,6 +249,15 @@
         }
     }
 
+    /*
+     * TODO(cleanup): It would make some sense if the result of
+     * preprocessing a message to determine the proper encoding (ie
+     * the resulting datastructure from calculateLength) could be
+     * passed as an argument to the actual final encoding function.
+     * This would better ensure that the logic behind size calculation
+     * actually matched the encoding.
+     */
+
     /**
      * Calculates the number of SMS's required to encode the message body and
      * the number of characters remaining until the next message.
@@ -232,46 +268,76 @@
      *         space chars.  If false, and if the messageBody contains
      *         non-7-bit encodable characters, length is calculated
      *         using a 16-bit encoding.
-     * @return an int[4] with int[0] being the number of SMS's required, int[1]
-     *         the number of code units used, and int[2] is the number of code
-     *         units remaining until the next message. int[3] is the encoding
-     *         type that should be used for the message.
+     * @return an int[4] with int[0] being the number of SMS's
+     *         required, int[1] the number of code units used, and
+     *         int[2] is the number of code units remaining until the
+     *         next message. int[3] is an indicator of the encoding
+     *         code unit size (see the ENCODING_* definitions in this
+     *         class).
      */
     public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
         int activePhone = TelephonyManager.getDefault().getPhoneType();
+        TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+            com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly) :
+            com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly);
         int ret[] = new int[4];
+        ret[0] = ted.msgCount;
+        ret[1] = ted.codeUnitCount;
+        ret[2] = ted.codeUnitsRemaining;
+        ret[3] = ted.codeUnitSize;
+        return ret;
+    }
 
-        int septets = (PHONE_TYPE_CDMA == activePhone) ?
-                com.android.internal.telephony.cdma.SmsMessage.calc7bitEncodedLength(msgBody,
-                        use7bitOnly) :
-                com.android.internal.telephony.gsm.SmsMessage.calc7bitEncodedLength(msgBody,
-                        use7bitOnly);
-        if (septets != -1) {
-            ret[1] = septets;
-            if (septets > MAX_USER_DATA_SEPTETS) {
-                ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
-                ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER
-                            - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
-            } else {
-                ret[0] = 1;
-                ret[2] = MAX_USER_DATA_SEPTETS - septets;
-            }
-            ret[3] = ENCODING_7BIT;
+    /**
+     * Divide a message text into several fragments, none bigger than
+     * the maximum SMS message text size.
+     *
+     * @param text text, must not be null.
+     * @return an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original msg text
+     */
+    public static ArrayList<String> fragmentText(String text) {
+        int activePhone = TelephonyManager.getDefault().getPhoneType();
+        TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+            com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false) :
+            com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false);
+
+        // TODO(cleanup): The code here could be rolled into the logic
+        // below cleanly if these MAX_* constants were defined more
+        // flexibly...
+
+        int limit;
+        if (ted.msgCount > 1) {
+            limit = (ted.codeUnitSize == ENCODING_7BIT) ?
+                MAX_USER_DATA_SEPTETS_WITH_HEADER : MAX_USER_DATA_BYTES_WITH_HEADER;
         } else {
-            int octets = msgBody.length() * 2;
-            ret[1] = msgBody.length();
-            if (octets > MAX_USER_DATA_BYTES) {
-                ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
-                ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER
-                            - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
-            } else {
-                ret[0] = 1;
-                ret[2] = (MAX_USER_DATA_BYTES - octets)/2;
-            }
-            ret[3] = ENCODING_16BIT;
+            limit = (ted.codeUnitSize == ENCODING_7BIT) ?
+                MAX_USER_DATA_SEPTETS : MAX_USER_DATA_BYTES;
         }
 
-        return ret;
+        int pos = 0;  // Index in code units.
+        int textLen = text.length();
+        ArrayList<String> result = new ArrayList<String>(ted.msgCount);
+        while (pos < textLen) {
+            int nextPos = 0;  // Counts code units.
+            if (ted.codeUnitSize == ENCODING_7BIT) {
+                if (PHONE_TYPE_CDMA == activePhone) {
+                    nextPos = pos + Math.min(limit, textLen - pos);
+                } else {
+                    nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
+                }
+            } else {  // Assume unicode.
+                nextPos = pos + Math.min(limit / 2, textLen - pos);
+            }
+            if ((nextPos <= pos) || (nextPos > textLen)) {
+                Log.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
+                          nextPos + " >= " + textLen + ")");
+                break;
+            }
+            result.add(text.substring(pos, nextPos));
+            pos = nextPos;
+        }
+        return result;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index 8e2941b..e8095e1 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -576,52 +576,6 @@
         return size;
     }
 
-    /**
-     * Returns the index into <code>s</code> of the first character
-     * after <code>limit</code> octets have been reached, starting at
-     * index <code>start</code>.  This is used when dividing messages
-     * in UCS2 encoding into units within the SMS message size limit.
-     *
-     * @param s source string
-     * @param start index of where to start counting septets
-     * @param limit maximum septets to include,
-     *   e.g. <code>MAX_USER_DATA_BYTES</code>
-     * @return index of first character that won't fit, or the length
-     *   of the entire string if everything fits
-     */
-    public static int
-    findUCS2LimitIndex(String s, int start, int limit) {
-        int numCharToBeEncoded = s.length() - start;
-        return ((numCharToBeEncoded*2 > limit)? limit/2: numCharToBeEncoded) + start;
-    }
-
-    /**
-     * Returns the index into <code>s</code> of the first character
-     * after <code>limit</code> septets/octets have been reached
-     * according to the <code>encodingType</code>, starting at
-     * index <code>start</code>.  This is used when dividing messages
-     * units within the SMS message size limit.
-     *
-     * @param s source string
-     * @param start index of where to start counting septets
-     * @param limit maximum septets to include,
-     *   e.g. <code>MAX_USER_DATA_BYTES</code>
-     * @return index of first character that won't fit, or the length
-     *   of the entire string if everything fits
-     */
-    public static int
-    findLimitIndex(String s, int start, int limit, int encodingType) throws EncodeException {
-        if (encodingType == SmsMessage.ENCODING_7BIT) {
-            return findGsmSeptetLimitIndex(s, start, limit);
-        }
-        else if (encodingType == SmsMessage.ENCODING_16BIT) {
-            return findUCS2LimitIndex(s, start, limit);
-        }
-        else {
-            throw new EncodeException("Unsupported encoding type: " + encodingType);
-        }
-    }
-
     // Set in the static initializer
     private static int sGsmSpaceChar;
 
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 4d32c35..3c7dd45 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -86,6 +86,38 @@
     /** TP-Message-Reference - Message Reference of sent message. @hide */
     public int messageRef;
 
+    /**
+     * For a specific text string, this object describes protocol
+     * properties of encoding it for transmission as message user
+     * data.
+     */
+    public static class TextEncodingDetails {
+        /**
+         *The number of SMS's required to encode the text.
+         */
+        public int msgCount;
+
+        /**
+         * The number of code units consumed so far, where code units
+         * are basically characters in the encoding -- for example,
+         * septets for the standard ASCII and GSM encodings, and 16
+         * bits for Unicode.
+         */
+        public int codeUnitCount;
+
+        /**
+         * How many code units are still available without spilling
+         * into an additional message.
+         */
+        public int codeUnitsRemaining;
+
+        /**
+         * The encoding code unit size (specified using
+         * android.telephony.SmsMessage ENCODING_*).
+         */
+        public int codeUnitSize;
+    }
+
     public static abstract class SubmitPduBase  {
         public byte[] encodedScAddress; // Null if not applicable.
         public byte[] encodedMessage;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 79e1cd6..2b4a700 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -34,6 +34,7 @@
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.cdma.SmsMessage;
 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
+import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.util.HexDump;
 
 import java.io.ByteArrayOutputStream;
@@ -302,8 +303,12 @@
                 deliveryIntent = deliveryIntents.get(i);
             }
 
-            SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(scAddr, destAddr,
-                    parts.get(i), deliveryIntent != null, smsHeader);
+            UserData uData = new UserData();
+            uData.payloadStr = parts.get(i);
+            uData.userDataHeader = smsHeader;
+
+            SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destAddr,
+                    uData, deliveryIntent != null);
 
             sendSubmitPdu(submitPdu, sentIntent, deliveryIntent);
         }
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index bbdd0dd..63d2c47 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -357,14 +357,18 @@
     }
 
     /**
-     * Calculate the number of septets needed to encode the message.
+     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
      *
-     * @param messageBody the message to encode
-     * @param force ignore (but still count) illegal characters if true
-     * @return septet count, or -1 on failure
+     * @param destAddr the address of the destination for the message
+     * @param userDara the data for the message
+     * @param statusReportRequested Indicates whether a report is requested for this message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
      */
-    public static int calc7bitEncodedLength(CharSequence msgBody, boolean force) {
-        return BearerData.calc7bitEncodedLength(msgBody.toString(), force);
+    public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
+            boolean statusReportRequested) {
+        return privateGetSubmitPdu(destAddr, statusReportRequested, userData);
     }
 
     /**
@@ -442,6 +446,18 @@
     }
 
     /**
+     * Calculate the number of septets needed to encode the message.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly ignore (but still count) illegal characters if true
+     * @return TextEncodingDetails
+     */
+    public static TextEncodingDetails calculateLength(CharSequence messageBody,
+            boolean use7bitOnly) {
+        return BearerData.calcTextEncodingDetails(messageBody.toString(), use7bitOnly);
+    }
+
+    /**
      * Returns the teleservice type of the message.
      * @return the teleservice:
      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_NOT_SET},
@@ -627,12 +643,15 @@
         bearerData.userData = userData;
         bearerData.hasUserDataHeader = (userData.userDataHeader != null);
 
+        int teleservice = bearerData.hasUserDataHeader ?
+                SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
+
         byte[] encodedBearerData = BearerData.encode(bearerData);
         if (encodedBearerData == null) return null;
 
         SmsEnvelope envelope = new SmsEnvelope();
         envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
-        envelope.teleService = SmsEnvelope.TELESERVICE_WMT;
+        envelope.teleService = teleservice;
         envelope.destAddress = destAddr;
         envelope.bearerReply = RETURN_ACK;
         envelope.bearerData = encodedBearerData;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 3c45aa4..a835dee 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.cdma.sms;
 
 import android.util.Log;
+import android.util.SparseIntArray;
 
 import android.telephony.SmsMessage;
 
@@ -26,6 +27,7 @@
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
 
 import com.android.internal.util.HexDump;
 import com.android.internal.util.BitwiseInputStream;
@@ -35,7 +37,7 @@
 /**
  * An object to encode and decode CDMA SMS bearer data.
  */
-public final class BearerData{
+public final class BearerData {
     private final static String LOG_TAG = "SMS";
 
     /**
@@ -385,56 +387,61 @@
         outStream.skip(3);
     }
 
-    private static class SeptetData {
-        byte data[];
-        int septetCount;
-
-        SeptetData(byte[] data, int septetCount) {
-            this.data = data;
-            this.septetCount = septetCount;
-        }
-    }
-
-    private static SeptetData encode7bitAscii(String msg, boolean force)
-        throws CodingException
-    {
-        try {
-            BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
-            byte[] expandedData = msg.getBytes("US-ASCII");
-            for (int i = 0; i < expandedData.length; i++) {
-                int charCode = expandedData[i];
-                // Test ourselves for ASCII membership, since Java seems not to care.
-                if ((charCode < UserData.PRINTABLE_ASCII_MIN_INDEX) ||
-                        (charCode > UserData.PRINTABLE_ASCII_MAX_INDEX)) {
-                    if (force) {
-                        outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
-                    } else {
-                        throw new CodingException("illegal ASCII code (" + charCode + ")");
-                    }
-                } else {
-                    outStream.write(7, expandedData[i]);
-                }
+    private static int countAsciiSeptets(CharSequence msg, boolean force) {
+        int msgLen = msg.length();
+        if (force) return msgLen;
+        for (int i = 0; i < msgLen; i++) {
+            if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) {
+                return -1;
             }
-            return new SeptetData(outStream.toByteArray(), expandedData.length);
-        } catch (java.io.UnsupportedEncodingException ex) {
-            throw new CodingException("7bit ASCII encode failed: " + ex);
-        } catch (BitwiseOutputStream.AccessException ex) {
-            throw new CodingException("7bit ASCII encode failed: " + ex);
         }
+        return msgLen;
     }
 
     /**
-     * Calculate the number of septets needed to encode the message.
+     * Calculate the message text encoding length, fragmentation, and other details.
      *
      * @param force ignore (but still count) illegal characters if true
      * @return septet count, or -1 on failure
      */
-    public static int calc7bitEncodedLength(String msg, boolean force) {
+    public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg,
+            boolean force7BitEncoding) {
+        TextEncodingDetails ted;
+        int septets = countAsciiSeptets(msg, force7BitEncoding);
+        if (septets != -1 && septets <= SmsMessage.MAX_USER_DATA_SEPTETS) {
+            ted = new TextEncodingDetails();
+            ted.msgCount = 1;
+            ted.codeUnitCount = septets;
+            ted.codeUnitsRemaining = SmsMessage.MAX_USER_DATA_SEPTETS - septets;
+            ted.codeUnitSize = SmsMessage.ENCODING_7BIT;
+        } else {
+            ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength(
+                    msg, force7BitEncoding);
+        }
+        return ted;
+    }
+
+    private static byte[] encode7bitAscii(String msg, boolean force)
+        throws CodingException
+    {
         try {
-            SeptetData data = encode7bitAscii(msg, force);
-            return data.septetCount;
-        } catch (CodingException ex) {
-            return -1;
+            BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
+            int msgLen = msg.length();
+            for (int i = 0; i < msgLen; i++) {
+                int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
+                if (charCode == -1) {
+                    if (force) {
+                        outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
+                    } else {
+                        throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
+                    }
+                } else {
+                    outStream.write(7, charCode);
+                }
+            }
+            return outStream.toByteArray();
+        } catch (BitwiseOutputStream.AccessException ex) {
+            throw new CodingException("7bit ASCII encode failed: " + ex);
         }
     }
 
@@ -452,8 +459,10 @@
         throws CodingException
     {
         try {
-            /**
-             * TODO(cleanup): find some way to do this without the copy.
+            /*
+             * TODO(cleanup): It would be nice if GsmAlphabet provided
+             * an option to produce just the data without prepending
+             * the length.
              */
             byte []fullData = GsmAlphabet.stringToGsm7BitPacked(msg);
             byte []data = new byte[fullData.length - 1];
@@ -470,54 +479,65 @@
         throws CodingException
     {
         byte[] headerData = null;
+        // TODO: if there is a header, meaning EMS mode, we probably
+        // also want the total UD length prior to the UDH length...
         if (uData.userDataHeader != null) headerData = SmsHeader.toByteArray(uData.userDataHeader);
         int headerDataLen = (headerData == null) ? 0 : headerData.length + 1;  // + length octet
 
         byte[] payloadData;
+        int codeUnitCount;
         if (uData.msgEncodingSet) {
             if (uData.msgEncoding == UserData.ENCODING_OCTET) {
                 if (uData.payload == null) {
                     Log.e(LOG_TAG, "user data with octet encoding but null payload");
-                    // TODO(code_review): reasonable for fail case? or maybe bail on encoding?
                     payloadData = new byte[0];
+                    codeUnitCount = 0;
                 } else {
                     payloadData = uData.payload;
+                    codeUnitCount = uData.payload.length;
                 }
             } else {
                 if (uData.payloadStr == null) {
                     Log.e(LOG_TAG, "non-octet user data with null payloadStr");
-                    // TODO(code_review): reasonable for fail case? or maybe bail on encoding?
                     uData.payloadStr = "";
                 }
                 if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
                     payloadData = encode7bitGsm(uData.payloadStr);
+                    codeUnitCount = (payloadData.length * 8) / 7;
                 } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
-                    SeptetData septetData = encode7bitAscii(uData.payloadStr, true);
-                    payloadData = septetData.data;
+                    payloadData = encode7bitAscii(uData.payloadStr, true);
+                    codeUnitCount = uData.payloadStr.length();
                 } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
                     payloadData = encodeUtf16(uData.payloadStr);
+                    codeUnitCount = uData.payloadStr.length();
                 } else {
                     throw new CodingException("unsupported user data encoding (" +
                                               uData.msgEncoding + ")");
                 }
-                uData.numFields = uData.payloadStr.length();
             }
         } else {
             if (uData.payloadStr == null) {
                 Log.e(LOG_TAG, "user data with null payloadStr");
-                // TODO(code_review): reasonable for fail case? or maybe bail on encoding?
                 uData.payloadStr = "";
             }
             try {
-                SeptetData septetData = encode7bitAscii(uData.payloadStr, false);
-                payloadData = septetData.data;
-                uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
+                if (headerData == null) {
+                    payloadData = encode7bitAscii(uData.payloadStr, false);
+                    codeUnitCount = uData.payloadStr.length();
+                    uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
+                } else {
+                    // If there is a header, we are in EMS mode, in
+                    // which case we use GSM encodings.
+                    payloadData = encode7bitGsm(uData.payloadStr);
+                    codeUnitCount = (payloadData.length * 8) / 7;
+                    uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+                }
             } catch (CodingException ex) {
                 payloadData = encodeUtf16(uData.payloadStr);
+                codeUnitCount = uData.payloadStr.length();
                 uData.msgEncoding = UserData.ENCODING_UNICODE_16;
             }
             uData.msgEncodingSet = true;
-            uData.numFields = uData.payloadStr.length();
         }
 
         int totalLength = payloadData.length + headerDataLen;
@@ -526,6 +546,7 @@
                                       " > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
         }
 
+        uData.numFields = codeUnitCount;
         uData.payload = new byte[totalLength];
         if (headerData != null) {
             uData.payload[0] = (byte)headerData.length;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index 8d4e769..d8a48cc 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.cdma.sms;
 
+import android.util.SparseIntArray;
+
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.util.HexDump;
 
@@ -40,6 +42,10 @@
     /**
      * IA5 data encoding character mappings.
      * (See CCITT Rec. T.50 Tables 1 and 3)
+     *
+     * Note this mapping is the the same as for printable ASCII
+     * characters, with a 0x20 offset, meaning that the ASCII SPACE
+     * character occurs with code 0x20.
      */
     public static final char[] IA5_MAP = {
         ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
@@ -61,7 +67,16 @@
      * Only elements between these indices in the ASCII table are printable.
      */
     public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20;
-    public static final int PRINTABLE_ASCII_MAX_INDEX = 0x7F;
+    public static final int ASCII_LF_INDEX = 0x0A;
+    public static final int ASCII_CR_INDEX = 0x0D;
+    public static final SparseIntArray charToAscii = new SparseIntArray();
+    static {
+        for (int i = 0; i < IA5_MAP.length; i++) {
+            charToAscii.put(IA5_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i);
+        }
+        charToAscii.put('\r', ASCII_LF_INDEX);
+        charToAscii.put('\n', ASCII_CR_INDEX);
+    }
 
     /**
      * Mapping for IA5 values less than 32 are flow control signals
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index a15bbdf..f1207e4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -26,6 +26,7 @@
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
 
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
@@ -742,17 +743,39 @@
     /**
      * Calculate the number of septets needed to encode the message.
      *
-     * @param messageBody the message to encode
-     * @param force ignore (but still count) illegal characters if true
-     * @return septet count, or -1 on failure
+     * @param msgBody the message to encode
+     * @param use7bitOnly ignore (but still count) illegal characters if true
+     * @return TextEncodingDetails
      */
-    public static int calc7bitEncodedLength(CharSequence messageBody, boolean force) {
+    public static TextEncodingDetails calculateLength(CharSequence msgBody,
+            boolean use7bitOnly) {
+        TextEncodingDetails ted = new TextEncodingDetails();
         try {
-            return GsmAlphabet.countGsmSeptets(messageBody, !force);
+            int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly);
+            ted.codeUnitCount = septets;
+            if (septets > MAX_USER_DATA_SEPTETS) {
+                ted.msgCount = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
+                ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS_WITH_HEADER
+                    - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
+            } else {
+                ted.msgCount = 1;
+                ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets;
+            }
+            ted.codeUnitSize = ENCODING_7BIT;
         } catch (EncodeException ex) {
-            /* Just fall through to the -1 error result below. */
+            int octets = msgBody.length() * 2;
+            ted.codeUnitCount = msgBody.length();
+            if (octets > MAX_USER_DATA_BYTES) {
+                ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
+                ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER
+                          - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+            } else {
+                ted.msgCount = 1;
+                ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
+            }
+            ted.codeUnitSize = ENCODING_16BIT;
         }
-        return -1;
+        return ted;
     }
 
     /** {@inheritDoc} */