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