Home | History | Annotate | Download | only in sms
      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