1 /* 2 * Copyright (C) 2015 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.messaging.sms; 18 19 import android.content.ContentValues; 20 import android.provider.Telephony; 21 22 import com.android.messaging.util.Assert; 23 import com.android.messaging.util.LogUtil; 24 import com.android.messaging.util.PhoneUtils; 25 import com.google.common.collect.Maps; 26 27 import org.xmlpull.v1.XmlPullParser; 28 import org.xmlpull.v1.XmlPullParserException; 29 30 import java.io.IOException; 31 import java.util.Map; 32 33 /* 34 * XML processor for the following files: 35 * 1. res/xml/apns.xml 36 * 2. res/xml/mms_config.xml (or related overlay files) 37 */ 38 class ApnsXmlProcessor { 39 public interface ApnHandler { 40 public void process(ContentValues apnValues); 41 } 42 43 public interface MmsConfigHandler { 44 public void process(String mccMnc, String key, String value, String type); 45 } 46 47 private static final String TAG = LogUtil.BUGLE_TAG; 48 49 private static final Map<String, String> APN_ATTRIBUTE_MAP = Maps.newHashMap(); 50 static { 51 APN_ATTRIBUTE_MAP.put("mcc", Telephony.Carriers.MCC); 52 APN_ATTRIBUTE_MAP.put("mnc", Telephony.Carriers.MNC); 53 APN_ATTRIBUTE_MAP.put("carrier", Telephony.Carriers.NAME); 54 APN_ATTRIBUTE_MAP.put("apn", Telephony.Carriers.APN); 55 APN_ATTRIBUTE_MAP.put("mmsc", Telephony.Carriers.MMSC); 56 APN_ATTRIBUTE_MAP.put("mmsproxy", Telephony.Carriers.MMSPROXY); 57 APN_ATTRIBUTE_MAP.put("mmsport", Telephony.Carriers.MMSPORT); 58 APN_ATTRIBUTE_MAP.put("type", Telephony.Carriers.TYPE); 59 APN_ATTRIBUTE_MAP.put("user", Telephony.Carriers.USER); 60 APN_ATTRIBUTE_MAP.put("password", Telephony.Carriers.PASSWORD); 61 APN_ATTRIBUTE_MAP.put("authtype", Telephony.Carriers.AUTH_TYPE); 62 APN_ATTRIBUTE_MAP.put("mvno_match_data", Telephony.Carriers.MVNO_MATCH_DATA); 63 APN_ATTRIBUTE_MAP.put("mvno_type", Telephony.Carriers.MVNO_TYPE); 64 APN_ATTRIBUTE_MAP.put("protocol", Telephony.Carriers.PROTOCOL); 65 APN_ATTRIBUTE_MAP.put("bearer", Telephony.Carriers.BEARER); 66 APN_ATTRIBUTE_MAP.put("server", Telephony.Carriers.SERVER); 67 APN_ATTRIBUTE_MAP.put("roaming_protocol", Telephony.Carriers.ROAMING_PROTOCOL); 68 APN_ATTRIBUTE_MAP.put("proxy", Telephony.Carriers.PROXY); 69 APN_ATTRIBUTE_MAP.put("port", Telephony.Carriers.PORT); 70 APN_ATTRIBUTE_MAP.put("carrier_enabled", Telephony.Carriers.CARRIER_ENABLED); 71 } 72 73 private static final String TAG_APNS = "apns"; 74 private static final String TAG_APN = "apn"; 75 private static final String TAG_MMS_CONFIG = "mms_config"; 76 77 // Handler to process one apn 78 private ApnHandler mApnHandler; 79 // Handler to process one mms_config key/value pair 80 private MmsConfigHandler mMmsConfigHandler; 81 82 private final StringBuilder mLogStringBuilder = new StringBuilder(); 83 84 private final XmlPullParser mInputParser; 85 86 private ApnsXmlProcessor(XmlPullParser parser) { 87 mInputParser = parser; 88 mApnHandler = null; 89 mMmsConfigHandler = null; 90 } 91 92 public static ApnsXmlProcessor get(XmlPullParser parser) { 93 Assert.notNull(parser); 94 return new ApnsXmlProcessor(parser); 95 } 96 97 public ApnsXmlProcessor setApnHandler(ApnHandler handler) { 98 mApnHandler = handler; 99 return this; 100 } 101 102 public ApnsXmlProcessor setMmsConfigHandler(MmsConfigHandler handler) { 103 mMmsConfigHandler = handler; 104 return this; 105 } 106 107 /** 108 * Move XML parser forward to next event type or the end of doc 109 * 110 * @param eventType 111 * @return The final event type we meet 112 * @throws XmlPullParserException 113 * @throws IOException 114 */ 115 private int advanceToNextEvent(int eventType) throws XmlPullParserException, IOException { 116 for (;;) { 117 int nextEvent = mInputParser.next(); 118 if (nextEvent == eventType 119 || nextEvent == XmlPullParser.END_DOCUMENT) { 120 return nextEvent; 121 } 122 } 123 } 124 125 public void process() { 126 try { 127 // Find the first element 128 if (advanceToNextEvent(XmlPullParser.START_TAG) != XmlPullParser.START_TAG) { 129 throw new XmlPullParserException("ApnsXmlProcessor: expecting start tag @" 130 + xmlParserDebugContext()); 131 } 132 // A single ContentValues object for holding the parsing result of 133 // an apn element 134 final ContentValues values = new ContentValues(); 135 String tagName = mInputParser.getName(); 136 // Top level tag can be "apns" (apns.xml) 137 // or "mms_config" (mms_config.xml) 138 if (TAG_APNS.equals(tagName)) { 139 // For "apns", there could be "apn" or both "apn" and "mms_config" 140 for (;;) { 141 if (advanceToNextEvent(XmlPullParser.START_TAG) != XmlPullParser.START_TAG) { 142 break; 143 } 144 tagName = mInputParser.getName(); 145 if (TAG_APN.equals(tagName)) { 146 processApn(values); 147 } else if (TAG_MMS_CONFIG.equals(tagName)) { 148 processMmsConfig(); 149 } 150 } 151 } else if (TAG_MMS_CONFIG.equals(tagName)) { 152 // mms_config.xml resource 153 processMmsConfig(); 154 } 155 } catch (IOException e) { 156 LogUtil.e(TAG, "ApnsXmlProcessor: I/O failure " + e, e); 157 } catch (XmlPullParserException e) { 158 LogUtil.e(TAG, "ApnsXmlProcessor: parsing failure " + e, e); 159 } 160 } 161 162 private Integer parseInt(String text, Integer defaultValue, String logHint) { 163 Integer value = defaultValue; 164 try { 165 value = Integer.parseInt(text); 166 } catch (Exception e) { 167 LogUtil.e(TAG, 168 "Invalid value " + text + "for" + logHint + " @" + xmlParserDebugContext()); 169 } 170 return value; 171 } 172 173 private Boolean parseBoolean(String text, Boolean defaultValue, String logHint) { 174 Boolean value = defaultValue; 175 try { 176 value = Boolean.parseBoolean(text); 177 } catch (Exception e) { 178 LogUtil.e(TAG, 179 "Invalid value " + text + "for" + logHint + " @" + xmlParserDebugContext()); 180 } 181 return value; 182 } 183 184 private static String xmlParserEventString(int event) { 185 switch (event) { 186 case XmlPullParser.START_DOCUMENT: return "START_DOCUMENT"; 187 case XmlPullParser.END_DOCUMENT: return "END_DOCUMENT"; 188 case XmlPullParser.START_TAG: return "START_TAG"; 189 case XmlPullParser.END_TAG: return "END_TAG"; 190 case XmlPullParser.TEXT: return "TEXT"; 191 } 192 return Integer.toString(event); 193 } 194 195 /** 196 * @return The debugging information of the parser's current position 197 */ 198 private String xmlParserDebugContext() { 199 mLogStringBuilder.setLength(0); 200 if (mInputParser != null) { 201 try { 202 final int eventType = mInputParser.getEventType(); 203 mLogStringBuilder.append(xmlParserEventString(eventType)); 204 if (eventType == XmlPullParser.START_TAG 205 || eventType == XmlPullParser.END_TAG 206 || eventType == XmlPullParser.TEXT) { 207 mLogStringBuilder.append('<').append(mInputParser.getName()); 208 for (int i = 0; i < mInputParser.getAttributeCount(); i++) { 209 mLogStringBuilder.append(' ') 210 .append(mInputParser.getAttributeName(i)) 211 .append('=') 212 .append(mInputParser.getAttributeValue(i)); 213 } 214 mLogStringBuilder.append("/>"); 215 } 216 return mLogStringBuilder.toString(); 217 } catch (XmlPullParserException e) { 218 LogUtil.e(TAG, "xmlParserDebugContext: " + e, e); 219 } 220 } 221 return "Unknown"; 222 } 223 224 /** 225 * Process one apn 226 * 227 * @param apnValues Where we store the parsed apn 228 * @throws IOException 229 * @throws XmlPullParserException 230 */ 231 private void processApn(ContentValues apnValues) throws IOException, XmlPullParserException { 232 Assert.notNull(apnValues); 233 apnValues.clear(); 234 // Collect all the attributes 235 for (int i = 0; i < mInputParser.getAttributeCount(); i++) { 236 final String key = APN_ATTRIBUTE_MAP.get(mInputParser.getAttributeName(i)); 237 if (key != null) { 238 apnValues.put(key, mInputParser.getAttributeValue(i)); 239 } 240 } 241 // Set numeric to be canonicalized mcc/mnc like "310120", always 6 digits 242 final String canonicalMccMnc = PhoneUtils.canonicalizeMccMnc( 243 apnValues.getAsString(Telephony.Carriers.MCC), 244 apnValues.getAsString(Telephony.Carriers.MNC)); 245 apnValues.put(Telephony.Carriers.NUMERIC, canonicalMccMnc); 246 // Some of the values should not be string type, converting them to desired types 247 final String authType = apnValues.getAsString(Telephony.Carriers.AUTH_TYPE); 248 if (authType != null) { 249 apnValues.put(Telephony.Carriers.AUTH_TYPE, parseInt(authType, -1, "apn authtype")); 250 } 251 final String carrierEnabled = apnValues.getAsString(Telephony.Carriers.CARRIER_ENABLED); 252 if (carrierEnabled != null) { 253 apnValues.put(Telephony.Carriers.CARRIER_ENABLED, 254 parseBoolean(carrierEnabled, null, "apn carrierEnabled")); 255 } 256 final String bearer = apnValues.getAsString(Telephony.Carriers.BEARER); 257 if (bearer != null) { 258 apnValues.put(Telephony.Carriers.BEARER, parseInt(bearer, 0, "apn bearer")); 259 } 260 // We are at the end tag 261 if (mInputParser.next() != XmlPullParser.END_TAG) { 262 throw new XmlPullParserException("Apn: expecting end tag @" 263 + xmlParserDebugContext()); 264 } 265 // We are done parsing one APN, call the handler 266 if (mApnHandler != null) { 267 mApnHandler.process(apnValues); 268 } 269 } 270 271 /** 272 * Process one mms_config. 273 * 274 * @throws IOException 275 * @throws XmlPullParserException 276 */ 277 private void processMmsConfig() 278 throws IOException, XmlPullParserException { 279 // Get the mcc and mnc attributes 280 final String canonicalMccMnc = PhoneUtils.canonicalizeMccMnc( 281 mInputParser.getAttributeValue(null, "mcc"), 282 mInputParser.getAttributeValue(null, "mnc")); 283 // We are at the start tag 284 for (;;) { 285 int nextEvent; 286 // Skipping spaces 287 while ((nextEvent = mInputParser.next()) == XmlPullParser.TEXT) { 288 } 289 if (nextEvent == XmlPullParser.START_TAG) { 290 // Parse one mms config key/value 291 processMmsConfigKeyValue(canonicalMccMnc); 292 } else if (nextEvent == XmlPullParser.END_TAG) { 293 break; 294 } else { 295 throw new XmlPullParserException("MmsConfig: expecting start or end tag @" 296 + xmlParserDebugContext()); 297 } 298 } 299 } 300 301 /** 302 * Process one mms_config key/value pair 303 * 304 * @param mccMnc The mcc and mnc of this mms_config 305 * @throws IOException 306 * @throws XmlPullParserException 307 */ 308 private void processMmsConfigKeyValue(String mccMnc) 309 throws IOException, XmlPullParserException { 310 final String key = mInputParser.getAttributeValue(null, "name"); 311 // We are at the start tag, the name of the tag is the type 312 // e.g. <int name="key">value</int> 313 final String type = mInputParser.getName(); 314 int nextEvent = mInputParser.next(); 315 String value = null; 316 if (nextEvent == XmlPullParser.TEXT) { 317 value = mInputParser.getText(); 318 nextEvent = mInputParser.next(); 319 } 320 if (nextEvent != XmlPullParser.END_TAG) { 321 throw new XmlPullParserException("ApnsXmlProcessor: expecting end tag @" 322 + xmlParserDebugContext()); 323 } 324 // We are done parsing one mms_config key/value, call the handler 325 if (mMmsConfigHandler != null) { 326 mMmsConfigHandler.process(mccMnc, key, value, type); 327 } 328 } 329 } 330