• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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;
18 
19 import android.content.res.Resources;
20 import android.content.res.XmlResourceParser;
21 import android.os.Build;
22 import android.telephony.Rlog;
23 import android.util.SparseIntArray;
24 
25 import com.android.internal.telephony.cdma.sms.UserData;
26 import com.android.internal.util.XmlUtils;
27 
28 public class Sms7BitEncodingTranslator {
29     private static final String TAG = "Sms7BitEncodingTranslator";
30     private static final boolean DBG = Build.IS_DEBUGGABLE ;
31     private static boolean mIs7BitTranslationTableLoaded = false;
32     private static SparseIntArray mTranslationTable = null;
33     private static SparseIntArray mTranslationTableCommon = null;
34     private static SparseIntArray mTranslationTableGSM = null;
35     private static SparseIntArray mTranslationTableCDMA = null;
36 
37     // Parser variables
38     private static final String XML_START_TAG = "SmsEnforce7BitTranslationTable";
39     private static final String XML_TRANSLATION_TYPE_TAG = "TranslationType";
40     private static final String XML_CHARACTOR_TAG = "Character";
41     private static final String XML_FROM_TAG = "from";
42     private static final String XML_TO_TAG = "to";
43 
44     /**
45      * Translates each message character that is not supported by GSM 7bit alphabet into a supported
46      * one.
47      *
48      * @param message message to be translated.
49      * @param isCdmaFormat true if cdma format should be used.
50      * @return translated message or null if some error occur.
51      */
translate(CharSequence message, boolean isCdmaFormat)52     public static String translate(CharSequence message, boolean isCdmaFormat) {
53         if (message == null) {
54             Rlog.w(TAG, "Null message can not be translated");
55             return null;
56         }
57 
58         int size = message.length();
59         if (size <= 0) {
60             return "";
61         }
62 
63         if (!mIs7BitTranslationTableLoaded) {
64             mTranslationTableCommon = new SparseIntArray();
65             mTranslationTableGSM = new SparseIntArray();
66             mTranslationTableCDMA = new SparseIntArray();
67             load7BitTranslationTableFromXml();
68             mIs7BitTranslationTableLoaded = true;
69         }
70 
71         if ((mTranslationTableCommon != null && mTranslationTableCommon.size() > 0) ||
72                 (mTranslationTableGSM != null && mTranslationTableGSM.size() > 0) ||
73                 (mTranslationTableCDMA != null && mTranslationTableCDMA.size() > 0)) {
74             char[] output = new char[size];
75             for (int i = 0; i < size; i++) {
76                 output[i] = translateIfNeeded(message.charAt(i), isCdmaFormat);
77             }
78 
79             return String.valueOf(output);
80         }
81 
82         return null;
83     }
84 
85     /**
86      * Translates a single character into its corresponding acceptable one, if
87      * needed, based on GSM 7-bit alphabet
88      *
89      * @param c
90      *            character to be translated
91      * @return original character, if it's present on GSM 7-bit alphabet; a
92      *         corresponding character, based on the translation table or white
93      *         space, if no mapping is found in the translation table for such
94      *         character
95      */
translateIfNeeded(char c, boolean isCdmaFormat)96     private static char translateIfNeeded(char c, boolean isCdmaFormat) {
97         if (noTranslationNeeded(c, isCdmaFormat)) {
98             if (DBG) {
99                 Rlog.v(TAG, "No translation needed for " + Integer.toHexString(c));
100             }
101             return c;
102         }
103 
104         /*
105          * Trying to translate unicode to Gsm 7-bit alphabet; If c is not
106          * present on translation table, c does not belong to Unicode Latin-1
107          * (Basic + Supplement), so we don't know how to translate it to a Gsm
108          * 7-bit character! We replace c for an empty space and advises the user
109          * about it.
110          */
111         int translation = -1;
112 
113         if (mTranslationTableCommon != null) {
114             translation = mTranslationTableCommon.get(c, -1);
115         }
116 
117         if (translation == -1) {
118             if (isCdmaFormat) {
119                 if (mTranslationTableCDMA != null) {
120                     translation = mTranslationTableCDMA.get(c, -1);
121                 }
122             } else {
123                 if (mTranslationTableGSM != null) {
124                     translation = mTranslationTableGSM.get(c, -1);
125                 }
126             }
127         }
128 
129         if (translation != -1) {
130             if (DBG) {
131                 Rlog.v(TAG, Integer.toHexString(c) + " (" + c + ")" + " translated to "
132                         + Integer.toHexString(translation) + " (" + (char) translation + ")");
133             }
134             return (char) translation;
135         } else {
136             if (DBG) {
137                 Rlog.w(TAG, "No translation found for " + Integer.toHexString(c)
138                         + "! Replacing for empty space");
139             }
140             return ' ';
141         }
142     }
143 
noTranslationNeeded(char c, boolean isCdmaFormat)144     private static boolean noTranslationNeeded(char c, boolean isCdmaFormat) {
145         if (isCdmaFormat) {
146             return GsmAlphabet.isGsmSeptets(c) && UserData.charToAscii.get(c, -1) != -1;
147         }
148         else {
149             return GsmAlphabet.isGsmSeptets(c);
150         }
151     }
152 
153     /**
154      * Load the whole translation table file from the framework resource
155      * encoded in XML.
156      */
load7BitTranslationTableFromXml()157     private static void load7BitTranslationTableFromXml() {
158         XmlResourceParser parser = null;
159         Resources r = Resources.getSystem();
160 
161         if (parser == null) {
162             if (DBG) Rlog.d(TAG, "load7BitTranslationTableFromXml: open normal file");
163             parser = r.getXml(com.android.internal.R.xml.sms_7bit_translation_table);
164         }
165 
166         try {
167             XmlUtils.beginDocument(parser, XML_START_TAG);
168             while (true)  {
169                 XmlUtils.nextElement(parser);
170                 String tag = parser.getName();
171                 if (DBG) {
172                     Rlog.d(TAG, "tag: " + tag);
173                 }
174                 if (XML_TRANSLATION_TYPE_TAG.equals(tag)) {
175                     String type = parser.getAttributeValue(null, "Type");
176                     if (DBG) {
177                         Rlog.d(TAG, "type: " + type);
178                     }
179                     if (type.equals("common")) {
180                         mTranslationTable = mTranslationTableCommon;
181                     } else if (type.equals("gsm")) {
182                         mTranslationTable = mTranslationTableGSM;
183                     } else if (type.equals("cdma")) {
184                         mTranslationTable = mTranslationTableCDMA;
185                     } else {
186                         Rlog.e(TAG, "Error Parsing 7BitTranslationTable: found incorrect type" + type);
187                     }
188                 } else if (XML_CHARACTOR_TAG.equals(tag) && mTranslationTable != null) {
189                     int from = parser.getAttributeUnsignedIntValue(null,
190                             XML_FROM_TAG, -1);
191                     int to = parser.getAttributeUnsignedIntValue(null,
192                             XML_TO_TAG, -1);
193                     if ((from != -1) && (to != -1)) {
194                         if (DBG) {
195                             Rlog.d(TAG, "Loading mapping " + Integer.toHexString(from)
196                                     .toUpperCase() + " -> " + Integer.toHexString(to)
197                                     .toUpperCase());
198                         }
199                         mTranslationTable.put (from, to);
200                     } else {
201                         Rlog.d(TAG, "Invalid translation table file format");
202                     }
203                 } else {
204                     break;
205                 }
206             }
207             if (DBG) Rlog.d(TAG, "load7BitTranslationTableFromXml: parsing successful, file loaded");
208         } catch (Exception e) {
209             Rlog.e(TAG, "Got exception while loading 7BitTranslationTable file.", e);
210         } finally {
211             if (parser instanceof XmlResourceParser) {
212                 ((XmlResourceParser)parser).close();
213             }
214         }
215     }
216 }
217