1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony.cdma.sms; 18 19 import android.util.SparseBooleanArray; 20 21 import com.android.internal.annotations.VisibleForTesting; 22 import com.android.internal.telephony.SmsAddress; 23 import com.android.internal.telephony.cdma.sms.UserData; 24 import com.android.internal.util.HexDump; 25 26 public class CdmaSmsAddress extends SmsAddress { 27 28 /** 29 * Digit Mode Indicator is a 1-bit value that indicates whether 30 * the address digits are 4-bit DTMF codes or 8-bit codes. (See 31 * 3GPP2 C.S0015-B, v2, 3.4.3.3) 32 */ 33 static public final int DIGIT_MODE_4BIT_DTMF = 0x00; 34 static public final int DIGIT_MODE_8BIT_CHAR = 0x01; 35 36 public int digitMode; 37 38 /** 39 * Number Mode Indicator is 1-bit value that indicates whether the 40 * address type is a data network address or not. (See 3GPP2 41 * C.S0015-B, v2, 3.4.3.3) 42 */ 43 static public final int NUMBER_MODE_NOT_DATA_NETWORK = 0x00; 44 static public final int NUMBER_MODE_DATA_NETWORK = 0x01; 45 46 public int numberMode; 47 48 /** 49 * Number Types for data networks. 50 * (See 3GPP2 C.S005-D, table2.7.1.3.2.4-2 for complete table) 51 * (See 3GPP2 C.S0015-B, v2, 3.4.3.3 for data network subset) 52 * NOTE: value is stored in the parent class ton field. 53 */ 54 static public final int TON_UNKNOWN = 0x00; 55 static public final int TON_INTERNATIONAL_OR_IP = 0x01; 56 static public final int TON_NATIONAL_OR_EMAIL = 0x02; 57 static public final int TON_NETWORK = 0x03; 58 static public final int TON_SUBSCRIBER = 0x04; 59 static public final int TON_ALPHANUMERIC = 0x05; 60 static public final int TON_ABBREVIATED = 0x06; 61 static public final int TON_RESERVED = 0x07; 62 63 /** 64 * Maximum lengths for fields as defined in ril_cdma_sms.h. 65 */ 66 static public final int SMS_ADDRESS_MAX = 36; 67 static public final int SMS_SUBADDRESS_MAX = 36; 68 69 /** 70 * This field shall be set to the number of address digits 71 * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) 72 */ 73 public int numberOfDigits; 74 75 /** 76 * Numbering Plan identification is a 0 or 4-bit value that 77 * indicates which numbering plan identification is set. (See 78 * 3GPP2, C.S0015-B, v2, 3.4.3.3 and C.S005-D, table2.7.1.3.2.4-3) 79 */ 80 static public final int NUMBERING_PLAN_UNKNOWN = 0x0; 81 static public final int NUMBERING_PLAN_ISDN_TELEPHONY = 0x1; 82 //static protected final int NUMBERING_PLAN_DATA = 0x3; 83 //static protected final int NUMBERING_PLAN_TELEX = 0x4; 84 //static protected final int NUMBERING_PLAN_PRIVATE = 0x9; 85 86 public int numberPlan; 87 88 /** 89 * NOTE: the parsed string address and the raw byte array values 90 * are stored in the parent class address and origBytes fields, 91 * respectively. 92 */ 93 CdmaSmsAddress()94 public CdmaSmsAddress(){ 95 } 96 97 @Override toString()98 public String toString() { 99 StringBuilder builder = new StringBuilder(); 100 builder.append("CdmaSmsAddress "); 101 builder.append("{ digitMode=" + digitMode); 102 builder.append(", numberMode=" + numberMode); 103 builder.append(", numberPlan=" + numberPlan); 104 builder.append(", numberOfDigits=" + numberOfDigits); 105 builder.append(", ton=" + ton); 106 builder.append(", address=\"" + address + "\""); 107 builder.append(", origBytes=" + HexDump.toHexString(origBytes)); 108 builder.append(" }"); 109 return builder.toString(); 110 } 111 112 /* 113 * TODO(cleanup): Refactor the parsing for addresses to better 114 * share code and logic with GSM. Also, gather all DTMF/BCD 115 * processing code in one place. 116 */ 117 @VisibleForTesting parseToDtmf(String address)118 public static byte[] parseToDtmf(String address) { 119 int digits = address.length(); 120 byte[] result = new byte[digits]; 121 for (int i = 0; i < digits; i++) { 122 char c = address.charAt(i); 123 int val = 0; 124 if ((c >= '1') && (c <= '9')) val = c - '0'; 125 else if (c == '0') val = 10; 126 else if (c == '*') val = 11; 127 else if (c == '#') val = 12; 128 else return null; 129 result[i] = (byte)val; 130 } 131 return result; 132 } 133 134 private static final char[] numericCharsDialable = { 135 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#' 136 }; 137 138 private static final char[] numericCharsSugar = { 139 '(', ')', ' ', '-', '+', '.', '/', '\\' 140 }; 141 142 private static final SparseBooleanArray numericCharDialableMap = new SparseBooleanArray ( 143 numericCharsDialable.length + numericCharsSugar.length); 144 static { 145 for (int i = 0; i < numericCharsDialable.length; i++) { numericCharDialableMap.put(numericCharsDialable[i], true)146 numericCharDialableMap.put(numericCharsDialable[i], true); 147 } 148 for (int i = 0; i < numericCharsSugar.length; i++) { numericCharDialableMap.put(numericCharsSugar[i], false)149 numericCharDialableMap.put(numericCharsSugar[i], false); 150 } 151 } 152 153 /** 154 * Given a numeric address string, return the string without 155 * syntactic sugar, meaning parens, spaces, hyphens/minuses, or 156 * plus signs. If the input string contains non-numeric 157 * non-punctuation characters, return null. 158 */ filterNumericSugar(String address)159 private static String filterNumericSugar(String address) { 160 StringBuilder builder = new StringBuilder(); 161 int len = address.length(); 162 for (int i = 0; i < len; i++) { 163 char c = address.charAt(i); 164 int mapIndex = numericCharDialableMap.indexOfKey(c); 165 if (mapIndex < 0) return null; 166 if (! numericCharDialableMap.valueAt(mapIndex)) continue; 167 builder.append(c); 168 } 169 return builder.toString(); 170 } 171 172 /** 173 * Given a string, return the string without whitespace, 174 * including CR/LF. 175 */ filterWhitespace(String address)176 private static String filterWhitespace(String address) { 177 StringBuilder builder = new StringBuilder(); 178 int len = address.length(); 179 for (int i = 0; i < len; i++) { 180 char c = address.charAt(i); 181 if ((c == ' ') || (c == '\r') || (c == '\n') || (c == '\t')) continue; 182 builder.append(c); 183 } 184 return builder.toString(); 185 } 186 187 /** 188 * Given a string, create a corresponding CdmaSmsAddress object. 189 * 190 * The result will be null if the input string is not 191 * representable using printable ASCII. 192 * 193 * For numeric addresses, the string is cleaned up by removing 194 * common punctuation. For alpha addresses, the string is cleaned 195 * up by removing whitespace. 196 */ parse(String address)197 public static CdmaSmsAddress parse(String address) { 198 CdmaSmsAddress addr = new CdmaSmsAddress(); 199 addr.address = address; 200 addr.ton = TON_UNKNOWN; 201 addr.digitMode = DIGIT_MODE_4BIT_DTMF; 202 addr.numberPlan = NUMBERING_PLAN_UNKNOWN; 203 addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK; 204 205 byte[] origBytes; 206 String filteredAddr = filterNumericSugar(address); 207 if (address.contains("+") || filteredAddr == null) { 208 // 3GPP2 C.S0015-B section 3.4.3.3 Address Parameters 209 // NUMBER_MODE should set to 1 for network address and email address. 210 addr.digitMode = DIGIT_MODE_8BIT_CHAR; 211 addr.numberMode = NUMBER_MODE_DATA_NETWORK; 212 filteredAddr = filterWhitespace(address); 213 214 if (address.contains("@")) { 215 // This is an email address 216 addr.ton = TON_NATIONAL_OR_EMAIL; 217 } else if (address.contains("+") && filterNumericSugar(address) != null) { 218 // This is an international number 219 // 3GPP2 C.S0015-B section 3.4.3.3 Address Parameters 220 // digit mode is set to 1 and number mode is set to 0, type of number should set 221 // to the value correspond to the value in 3GPP2 C.S005-D, table2.7.1.3.2.4-2 222 addr.ton = TON_INTERNATIONAL_OR_IP; 223 addr.numberPlan = NUMBERING_PLAN_ISDN_TELEPHONY; 224 addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK; 225 filteredAddr = filterNumericSugar(address); 226 } 227 228 origBytes = UserData.stringToAscii(filteredAddr); 229 } else { 230 // The address is not an international number and it only contains digit and *# 231 origBytes = parseToDtmf(filteredAddr); 232 } 233 234 if (origBytes == null) { 235 return null; 236 } 237 238 addr.origBytes = origBytes; 239 addr.numberOfDigits = origBytes.length; 240 return addr; 241 } 242 } 243