Home | History | Annotate | Download | only in carrierconfig
      1 package com.android.carrierconfig;
      2 
      3 import android.content.Context;
      4 import android.os.Build;
      5 import android.os.PersistableBundle;
      6 import android.service.carrier.CarrierIdentifier;
      7 import android.service.carrier.CarrierService;
      8 import android.telephony.CarrierConfigManager;
      9 import android.telephony.TelephonyManager;
     10 import android.util.Log;
     11 
     12 import org.xmlpull.v1.XmlPullParser;
     13 import org.xmlpull.v1.XmlPullParserException;
     14 import org.xmlpull.v1.XmlPullParserFactory;
     15 
     16 import java.io.File;
     17 import java.io.FileOutputStream;
     18 import java.io.IOException;
     19 import java.io.InputStream;
     20 import java.util.HashMap;
     21 
     22 import com.android.internal.util.FastXmlSerializer;
     23 
     24 /**
     25  * Provides network overrides for carrier configuration.
     26  *
     27  * The configuration available through CarrierConfigManager is a combination of default values,
     28  * default network overrides, and carrier overrides. The default network overrides are provided by
     29  * this service. For a given network, we look for a matching XML file in our assets folder, and
     30  * return the PersistableBundle from that file. Assets are preferred over Resources because resource
     31  * overlays only support using MCC+MNC and that doesn't work with MVNOs. The only resource file used
     32  * is vendor.xml, to provide vendor-specific overrides.
     33  */
     34 public class DefaultCarrierConfigService extends CarrierService {
     35 
     36     private static final String TAG = "DefaultCarrierConfigService";
     37 
     38     private XmlPullParserFactory mFactory;
     39 
     40     public DefaultCarrierConfigService() {
     41         Log.d(TAG, "Service created");
     42         mFactory = null;
     43     }
     44 
     45     /**
     46      * Returns per-network overrides for carrier configuration.
     47      *
     48      * This returns a carrier config bundle appropriate for the given network by reading data from
     49      * files in our assets folder. First we look for a file named after the MCC+MNC of {@code id}
     50      * and then we read res/xml/vendor.xml. Both files may contain multiple bundles with filters on
     51      * them. All the matching bundles are flattened to return one carrier config bundle.
     52      */
     53     @Override
     54     public PersistableBundle onLoadConfig(CarrierIdentifier id) {
     55         Log.d(TAG, "Config being fetched");
     56 
     57         if (id == null) {
     58             return null;
     59         }
     60 
     61 
     62         PersistableBundle config = null;
     63         try {
     64             synchronized (this) {
     65                 if (mFactory == null) {
     66                     mFactory = XmlPullParserFactory.newInstance();
     67                 }
     68             }
     69 
     70             XmlPullParser parser = mFactory.newPullParser();
     71             String fileName = "carrier_config_" + id.getMcc() + id.getMnc() + ".xml";
     72             parser.setInput(getApplicationContext().getAssets().open(fileName), "utf-8");
     73             config = readConfigFromXml(parser, id);
     74         }
     75         catch (IOException | XmlPullParserException e) {
     76             Log.d(TAG, e.toString());
     77             // We can return an empty config for unknown networks.
     78             config = new PersistableBundle();
     79         }
     80 
     81         // Treat vendor.xml as if it were appended to the carrier config file we read.
     82         XmlPullParser vendorInput = getApplicationContext().getResources().getXml(R.xml.vendor);
     83         try {
     84             PersistableBundle vendorConfig = readConfigFromXml(vendorInput, id);
     85             config.putAll(vendorConfig);
     86         }
     87         catch (IOException | XmlPullParserException e) {
     88             Log.e(TAG, e.toString());
     89         }
     90 
     91         return config;
     92     }
     93 
     94     /**
     95      * Parses an XML document and returns a PersistableBundle.
     96      *
     97      * <p>This function iterates over each {@code <carrier_config>} node in the XML document and
     98      * parses it into a bundle if its filters match {@code id}. The format of XML bundles is defined
     99      * by {@link PersistableBundle#restoreFromXml}. All the matching bundles will be flattened and
    100      * returned as a single bundle.</p>
    101      *
    102      * <p>Here is an example document. The second bundle will be applied to the first only if the
    103      * GID1 is ABCD.
    104      * <pre>{@code
    105      * <carrier_config_list>
    106      *     <carrier_config>
    107      *         <boolean name="voicemail_notification_persistent_bool" value="true" />
    108      *     </carrier_config>
    109      *     <carrier_config gid1="ABCD">
    110      *         <boolean name="voicemail_notification_persistent_bool" value="false" />
    111      *     </carrier_config>
    112      * </carrier_config_list>
    113      * }</pre></p>
    114      *
    115      * @param parser an XmlPullParser pointing at the beginning of the document.
    116      * @param id the details of the SIM operator used to filter parts of the document
    117      * @return a possibly empty PersistableBundle containing the config values.
    118      */
    119     static PersistableBundle readConfigFromXml(XmlPullParser parser, CarrierIdentifier id)
    120             throws IOException, XmlPullParserException {
    121         PersistableBundle config = new PersistableBundle();
    122 
    123         if (parser == null) {
    124           return config;
    125         }
    126 
    127         // Iterate over each <carrier_config> node in the document and add it to the returned
    128         // bundle if its filters match.
    129         int event;
    130         while (((event = parser.next()) != XmlPullParser.END_DOCUMENT)) {
    131             if (event == XmlPullParser.START_TAG && "carrier_config".equals(parser.getName())) {
    132                 // Skip this fragment if it has filters that don't match.
    133                 if (!checkFilters(parser, id)) {
    134                     continue;
    135                 }
    136                 PersistableBundle configFragment = PersistableBundle.restoreFromXml(parser);
    137                 config.putAll(configFragment);
    138             }
    139         }
    140 
    141         return config;
    142     }
    143 
    144     /**
    145      * Checks to see if an XML node matches carrier filters.
    146      *
    147      * <p>This iterates over the attributes of the current tag pointed to by {@code parser} and
    148      * checks each one against {@code id} or {@link Build.DEVICE}. Attributes that are not specified
    149      * in the node will not be checked, so a node with no attributes will always return true. The
    150      * supported filter attributes are,
    151      * <ul>
    152      *   <li>mcc: {@link CarrierIdentifier#getMcc}</li>
    153      *   <li>mnc: {@link CarrierIdentifier#getMnc}</li>
    154      *   <li>gid1: {@link CarrierIdentifier#getGid1}</li>
    155      *   <li>gid2: {@link CarrierIdentifier#getGid2}</li>
    156      *   <li>spn: {@link CarrierIdentifier#getSpn}</li>
    157      *   <li>device: {@link Build.DEVICE}</li>
    158      * </ul>
    159      * </p>
    160      *
    161      * @param parser an XmlPullParser pointing at a START_TAG with the attributes to check.
    162      * @param id the carrier details to check against.
    163      * @return false if any XML attribute does not match the corresponding value.
    164      */
    165     static boolean checkFilters(XmlPullParser parser, CarrierIdentifier id) {
    166         boolean result = true;
    167         for (int i = 0; i < parser.getAttributeCount(); ++i) {
    168             String attribute = parser.getAttributeName(i);
    169             String value = parser.getAttributeValue(i);
    170             switch (attribute) {
    171                 case "mcc":
    172                     result = result && value.equals(id.getMcc());
    173                     break;
    174                 case "mnc":
    175                     result = result && value.equals(id.getMnc());
    176                     break;
    177                 case "gid1":
    178                     result = result && value.equals(id.getGid1());
    179                     break;
    180                 case "gid2":
    181                     result = result && value.equals(id.getGid2());
    182                     break;
    183                 case "spn":
    184                     result = result && value.equals(id.getSpn());
    185                     break;
    186                 case "device":
    187                     result = result && value.equals(Build.DEVICE);
    188                     break;
    189                 default:
    190                     Log.e(TAG, "Unknown attribute " + attribute + "=" + value);
    191                     result = false;
    192                     break;
    193             }
    194         }
    195         return result;
    196     }
    197 }
    198