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 DefaultCarrierConfigService()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 onLoadConfig(CarrierIdentifier id)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 */ readConfigFromXml(XmlPullParser parser, CarrierIdentifier id)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 */ checkFilters(XmlPullParser parser, CarrierIdentifier id)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