From 1424fe0059a3fe163fb98bad807a1eb635b0a161 Mon Sep 17 00:00:00 2001 From: Tammo Spalink Date: Thu, 23 Jul 2009 18:51:17 +0800 Subject: [PATCH] added non-numeric cdma sms address support addresses bug http://buganizer/issue?id=1992198 --- .../internal/telephony/cdma/SmsMessage.java | 40 +----- .../telephony/cdma/sms/BearerData.java | 6 + .../telephony/cdma/sms/CdmaSmsAddress.java | 125 +++++++++++++++++- .../internal/telephony/cdma/sms/UserData.java | 20 +++ .../com/android/unit_tests/CdmaSmsTest.java | 61 +++++++++ 5 files changed, 217 insertions(+), 35 deletions(-) diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index c085739fd7aca..30adc524737ff 100755 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -584,38 +584,6 @@ public class SmsMessage extends SmsMessageBase { } } - private static CdmaSmsAddress parseCdmaSmsAddr(String addrStr) { - // see C.S0015-B, v2.0, 3.4.3.3 - CdmaSmsAddress addr = new CdmaSmsAddress(); - addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF; - try { - addr.origBytes = addrStr.getBytes("UTF-8"); - for (int index = 0; index < addr.origBytes.length; index++) { - if (addr.origBytes[index] >= '0' && addr.origBytes[index] <= '9') { - if (addr.origBytes[index] == '0') { - addr.origBytes[index] = 10; - } else { - addr.origBytes[index] -= '0'; - } - } else if (addr.origBytes[index] == '*') { - addr.origBytes[index] = 11; - } else if (addr.origBytes[index] == '#') { - addr.origBytes[index] = 12; - } else { - return null; - } - } - } catch (java.io.UnsupportedEncodingException ex) { - Log.e(LOG_TAG, "CDMA address parsing failed: " + ex); - return null; - } - addr.numberOfDigits = (byte)addr.origBytes.length; - addr.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK; - addr.numberPlan = CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY; - addr.ton = CdmaSmsAddress.TON_UNKNOWN; - return addr; - } - /** * Set the nextMessageId to a random value between 0 and 65536 * See C.S0015-B, v2.0, 4.3.1.5 @@ -642,7 +610,13 @@ public class SmsMessage extends SmsMessageBase { * TODO(cleanup): give this function a more meaningful name. */ - CdmaSmsAddress destAddr = parseCdmaSmsAddr(destAddrStr); + /** + * TODO(cleanup): Make returning null from the getSubmitPdu + * variations meaningful -- clean up the error feedback + * mechanism, and avoid null pointer exceptions. + */ + + CdmaSmsAddress destAddr = CdmaSmsAddress.parse(destAddrStr); if (destAddr == null) return null; BearerData bearerData = new BearerData(); 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 2b32872dd198e..13cea990bdf01 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -624,6 +624,12 @@ public final class BearerData { return rawData; } + /* + * TODO(cleanup): CdmaSmsAddress encoding should make use of + * CdmaSmsAddress.parse provided that DTMF encoding is unified, + * and the difference in 4bit vs 8bit is resolved. + */ + private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException { if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { try { diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java index 4d7996657d6d6..d9cc2c62d1505 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java @@ -16,7 +16,10 @@ package com.android.internal.telephony.cdma.sms; +import android.util.SparseBooleanArray; + import com.android.internal.telephony.SmsAddress; +import com.android.internal.telephony.cdma.sms.UserData; import com.android.internal.util.HexDump; public class CdmaSmsAddress extends SmsAddress { @@ -43,7 +46,8 @@ public class CdmaSmsAddress extends SmsAddress { /** * Number Types for data networks. - * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + * (See 3GPP2 C.S005-D, table2.7.1.3.2.4-2 for complete table) + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3 for data network subset) * NOTE: value is stored in the parent class ton field. */ static public final int TON_UNKNOWN = 0x00; @@ -98,10 +102,127 @@ public class CdmaSmsAddress extends SmsAddress { builder.append(", numberPlan=" + numberPlan); builder.append(", numberOfDigits=" + numberOfDigits); builder.append(", ton=" + ton); - builder.append(", address=" + address); + builder.append(", address=\"" + address + "\""); builder.append(", origBytes=" + HexDump.toHexString(origBytes)); builder.append(" }"); return builder.toString(); } + /* + * TODO(cleanup): Refactor the parsing for addresses to better + * share code and logic with GSM. Also, gather all DTMF/BCD + * processing code in one place. + */ + + private static byte[] parseToDtmf(String address) { + int digits = address.length(); + byte[] result = new byte[digits]; + for (int i = 0; i < digits; i++) { + char c = address.charAt(i); + int val = 0; + if ((c >= '1') && (c <= '9')) val = c - '0'; + else if (c == '0') val = 10; + else if (c == '*') val = 11; + else if (c == '#') val = 12; + else return null; + result[i] = (byte)val; + } + return result; + } + + private static final char[] numericCharsDialable = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#' + }; + + private static final char[] numericCharsSugar = { + '(', ')', ' ', '-', '+' + }; + + private static final SparseBooleanArray numericCharDialableMap = new SparseBooleanArray ( + numericCharsDialable.length + numericCharsSugar.length); + static { + for (int i = 0; i < numericCharsDialable.length; i++) { + numericCharDialableMap.put(numericCharsDialable[i], true); + } + for (int i = 0; i < numericCharsSugar.length; i++) { + numericCharDialableMap.put(numericCharsSugar[i], false); + } + } + + /** + * Given a numeric address string, return the string without + * syntactic sugar, meaning parens, spaces, hyphens/minuses, or + * plus signs. If the input string contains non-numeric + * non-punctuation characters, return null. + */ + private static String filterNumericSugar(String address) { + StringBuilder builder = new StringBuilder(); + int len = address.length(); + for (int i = 0; i < len; i++) { + char c = address.charAt(i); + int mapIndex = numericCharDialableMap.indexOfKey(c); + if (mapIndex < 0) return null; + if (! numericCharDialableMap.valueAt(mapIndex)) continue; + builder.append(c); + } + return builder.toString(); + } + + /** + * Given a string, return the string without whitespace, + * including CR/LF. + */ + private static String filterWhitespace(String address) { + StringBuilder builder = new StringBuilder(); + int len = address.length(); + for (int i = 0; i < len; i++) { + char c = address.charAt(i); + if ((c == ' ') || (c == '\r') || (c == '\n') || (c == '\t')) continue; + builder.append(c); + } + return builder.toString(); + } + + /** + * Given a string, create a corresponding CdmaSmsAddress object. + * + * The result will be null if the input string is not + * representable using printable ASCII. + * + * For numeric addresses, the string is cleaned up by removing + * common punctuation. For alpha addresses, the string is cleaned + * up by removing whitespace. + */ + public static CdmaSmsAddress parse(String address) { + CdmaSmsAddress addr = new CdmaSmsAddress(); + addr.address = address; + addr.ton = CdmaSmsAddress.TON_UNKNOWN; + byte[] origBytes = null; + String filteredAddr = filterNumericSugar(address); + if (filteredAddr != null) { + origBytes = parseToDtmf(filteredAddr); + } + if (origBytes != null) { + addr.digitMode = DIGIT_MODE_4BIT_DTMF; + addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK; + if (address.indexOf('+') != -1) { + addr.ton = TON_INTERNATIONAL_OR_IP; + } + } else { + filteredAddr = filterWhitespace(address); + origBytes = UserData.stringToAscii(filteredAddr); + if (origBytes == null) { + return null; + } + addr.digitMode = DIGIT_MODE_8BIT_CHAR; + addr.numberMode = NUMBER_MODE_DATA_NETWORK; + if (address.indexOf('@') != -1) { + addr.ton = TON_NATIONAL_OR_EMAIL; + } + } + addr.origBytes = origBytes; + addr.numberOfDigits = origBytes.length; + return addr; + } + } 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 04796b8480ff9..54c1f80340e23 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java @@ -92,6 +92,26 @@ public class UserData { charToAscii.put('\n', ASCII_CR_INDEX); } + /* + * TODO(cleanup): Move this very generic functionality somewhere + * more general. + */ + /** + * Given a string generate a corresponding ASCII-encoded byte + * array, but limited to printable characters. If the input + * contains unprintable characters, return null. + */ + public static byte[] stringToAscii(String str) { + int len = str.length(); + byte[] result = new byte[len]; + for (int i = 0; i < len; i++) { + int charCode = charToAscii.get(str.charAt(i), -1); + if (charCode == -1) return null; + result[i] = (byte)charCode; + } + return result; + } + /** * Mapping for IA5 values less than 32 are flow control signals * and not used here. diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java index f0ba5737d884d..90a29179c9e24 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java @@ -37,6 +37,67 @@ import android.util.Log; public class CdmaSmsTest extends AndroidTestCase { private final static String LOG_TAG = "CDMA"; + @SmallTest + public void testCdmaSmsAddrParsing() throws Exception { + CdmaSmsAddress addr = CdmaSmsAddress.parse("6502531000"); + assertEquals(addr.ton, CdmaSmsAddress.TON_UNKNOWN); + assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF); + assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK); + assertEquals(addr.numberOfDigits, 10); + assertEquals(addr.origBytes.length, 10); + byte[] data = {6, 5, 10, 2, 5, 3, 1, 10, 10, 10}; + for (int i = 0; i < data.length; i++) { + assertEquals(addr.origBytes[i], data[i]); + } + addr = CdmaSmsAddress.parse("(650) 253-1000"); + assertEquals(addr.ton, CdmaSmsAddress.TON_UNKNOWN); + assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF); + assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK); + assertEquals(addr.numberOfDigits, 10); + assertEquals(addr.origBytes.length, 10); + byte[] data2 = {6, 5, 10, 2, 5, 3, 1, 10, 10, 10}; + for (int i = 0; i < data2.length; i++) { + assertEquals(addr.origBytes[i], data2[i]); + } + addr = CdmaSmsAddress.parse("(+886) 917 222 555"); + assertEquals(addr.ton, CdmaSmsAddress.TON_INTERNATIONAL_OR_IP); + assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF); + assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK); + assertEquals(addr.numberOfDigits, 12); + assertEquals(addr.origBytes.length, 12); + byte[] data3 = {8, 8, 6, 9, 1, 7, 2, 2, 2, 5, 5, 5}; + for (int i = 0; i < data3.length; i++) { + assertEquals(addr.origBytes[i], data3[i]); + } + addr = CdmaSmsAddress.parse("(650) *253-1000 #600"); + byte[] data4 = {6, 5, 10, 11, 2, 5, 3, 1, 10, 10, 10, 12, 6, 10, 10}; + for (int i = 0; i < data4.length; i++) { + assertEquals(addr.origBytes[i], data4[i]); + } + String input = "x@y.com,a@b.com"; + addr = CdmaSmsAddress.parse(input); + assertEquals(addr.ton, CdmaSmsAddress.TON_NATIONAL_OR_EMAIL); + assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR); + assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK); + assertEquals(addr.numberOfDigits, 15); + assertEquals(addr.origBytes.length, 15); + assertEquals(new String(addr.origBytes), input); + addr = CdmaSmsAddress.parse("foo bar"); + assertEquals(addr.ton, CdmaSmsAddress.TON_UNKNOWN); + assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR); + assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK); + assertEquals(addr.numberOfDigits, 6); + assertEquals(addr.origBytes.length, 6); + assertEquals(new String(addr.origBytes), "foobar"); + addr = CdmaSmsAddress.parse("f\noo\tb a\rr"); + assertEquals(new String(addr.origBytes), "foobar"); + assertEquals(CdmaSmsAddress.parse("f\u0000oo bar"), null); + assertEquals(CdmaSmsAddress.parse("f\u0007oo bar"), null); + assertEquals(CdmaSmsAddress.parse("f\u0080oo bar"), null); + assertEquals(CdmaSmsAddress.parse("f\u1ECFboo\u001fbar"), null); + assertEquals(CdmaSmsAddress.parse("f\u0080oo bar"), null); + } + @SmallTest public void testUserData7bitGsm() throws Exception { String pdu = "00031040900112488ea794e074d69e1b7392c270326cde9e98";