13a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseripackage com.android.carrierconfig; 23a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri 3449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport android.content.Context; 4449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport android.os.Build; 5202b0f4b6573d116a753332a7b1ad996d8eece20Jonathan Basseriimport android.os.PersistableBundle; 63a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseriimport android.service.carrier.CarrierIdentifier; 7449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport android.service.carrier.CarrierService; 83a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseriimport android.telephony.CarrierConfigManager; 92070ccf11e03b65a877e8007de9551598d6aae63Nancy Chenimport android.telephony.TelephonyManager; 103a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseriimport android.util.Log; 113a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri 12449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport org.xmlpull.v1.XmlPullParser; 13449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport org.xmlpull.v1.XmlPullParserException; 146c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseriimport org.xmlpull.v1.XmlPullParserFactory; 153a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri 16449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport java.io.File; 17449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport java.io.FileOutputStream; 18449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport java.io.IOException; 19449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport java.io.InputStream; 20449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport java.util.HashMap; 213a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri 22449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseriimport com.android.internal.util.FastXmlSerializer; 233a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri 24449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri/** 25449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * Provides network overrides for carrier configuration. 26449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * 27449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * The configuration available through CarrierConfigManager is a combination of default values, 28449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * default network overrides, and carrier overrides. The default network overrides are provided by 29449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * this service. For a given network, we look for a matching XML file in our assets folder, and 30449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * return the PersistableBundle from that file. Assets are preferred over Resources because resource 31449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * overlays only support using MCC+MNC and that doesn't work with MVNOs. The only resource file used 32449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * is vendor.xml, to provide vendor-specific overrides. 33449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri */ 34449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseripublic class DefaultCarrierConfigService extends CarrierService { 3533d11a4788d23426a0b428db683f58d564bd87ceJonathan Basseri 36449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri private static final String TAG = "DefaultCarrierConfigService"; 373a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri 386c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri private XmlPullParserFactory mFactory; 396c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri 403a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri public DefaultCarrierConfigService() { 413a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri Log.d(TAG, "Service created"); 426c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri mFactory = null; 433a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri } 443a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri 45449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri /** 46449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * Returns per-network overrides for carrier configuration. 47449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * 48449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * This returns a carrier config bundle appropriate for the given network by reading data from 49449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * files in our assets folder. First we look for a file named after the MCC+MNC of {@code id} 50449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * and then we read res/xml/vendor.xml. Both files may contain multiple bundles with filters on 51449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * them. All the matching bundles are flattened to return one carrier config bundle. 52449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri */ 533a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri @Override 54202b0f4b6573d116a753332a7b1ad996d8eece20Jonathan Basseri public PersistableBundle onLoadConfig(CarrierIdentifier id) { 553a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri Log.d(TAG, "Config being fetched"); 56449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri 57449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri if (id == null) { 58449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri return null; 59449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 60449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri 61eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri 62eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri PersistableBundle config = null; 63449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri try { 646c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri synchronized (this) { 656c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri if (mFactory == null) { 666c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri mFactory = XmlPullParserFactory.newInstance(); 676c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri } 686c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri } 69eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri 706c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri XmlPullParser parser = mFactory.newPullParser(); 71eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri String fileName = "carrier_config_" + id.getMcc() + id.getMnc() + ".xml"; 726c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri parser.setInput(getApplicationContext().getAssets().open(fileName), "utf-8"); 73eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri config = readConfigFromXml(parser, id); 74eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri } 75eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri catch (IOException | XmlPullParserException e) { 76eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri Log.d(TAG, e.toString()); 77eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri // We can return an empty config for unknown networks. 78eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri config = new PersistableBundle(); 79eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri } 80eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri 81eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri // Treat vendor.xml as if it were appended to the carrier config file we read. 82eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri XmlPullParser vendorInput = getApplicationContext().getResources().getXml(R.xml.vendor); 83eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri try { 84449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri PersistableBundle vendorConfig = readConfigFromXml(vendorInput, id); 85449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri config.putAll(vendorConfig); 86449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 876c8cffb50a5e21cbadbf715a69a57c749ef2626fJonathan Basseri catch (IOException | XmlPullParserException e) { 88449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri Log.e(TAG, e.toString()); 89449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 90eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri 91eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri return config; 92449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 93449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri 94449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri /** 95449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * Parses an XML document and returns a PersistableBundle. 96449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * 97eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri * <p>This function iterates over each {@code <carrier_config>} node in the XML document and 98eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri * parses it into a bundle if its filters match {@code id}. The format of XML bundles is defined 99eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri * by {@link PersistableBundle#restoreFromXml}. All the matching bundles will be flattened and 100449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * returned as a single bundle.</p> 101449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * 102449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <p>Here is an example document. The second bundle will be applied to the first only if the 103449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * GID1 is ABCD. 104449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <pre>{@code 105449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <carrier_config_list> 106449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <carrier_config> 107449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <boolean name="voicemail_notification_persistent_bool" value="true" /> 108449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * </carrier_config> 109449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <carrier_config gid1="ABCD"> 110449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <boolean name="voicemail_notification_persistent_bool" value="false" /> 111449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * </carrier_config> 112449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * </carrier_config_list> 113449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * }</pre></p> 114449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * 115449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * @param parser an XmlPullParser pointing at the beginning of the document. 116449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * @param id the details of the SIM operator used to filter parts of the document 117449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * @return a possibly empty PersistableBundle containing the config values. 118449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri */ 119eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri static PersistableBundle readConfigFromXml(XmlPullParser parser, CarrierIdentifier id) 120eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri throws IOException, XmlPullParserException { 121449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri PersistableBundle config = new PersistableBundle(); 122449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri 123449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri if (parser == null) { 124449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri return config; 125449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 126449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri 127eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri // Iterate over each <carrier_config> node in the document and add it to the returned 128eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri // bundle if its filters match. 129eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri int event; 130eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri while (((event = parser.next()) != XmlPullParser.END_DOCUMENT)) { 131eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri if (event == XmlPullParser.START_TAG && "carrier_config".equals(parser.getName())) { 132eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri // Skip this fragment if it has filters that don't match. 133eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri if (!checkFilters(parser, id)) { 134eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri continue; 135449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 136eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri PersistableBundle configFragment = PersistableBundle.restoreFromXml(parser); 137eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri config.putAll(configFragment); 138449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 139449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 140449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri 141449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri return config; 142449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 143449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri 144449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri /** 145449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * Checks to see if an XML node matches carrier filters. 146449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * 147eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri * <p>This iterates over the attributes of the current tag pointed to by {@code parser} and 148eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri * checks each one against {@code id} or {@link Build.DEVICE}. Attributes that are not specified 149eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri * in the node will not be checked, so a node with no attributes will always return true. The 150eb8ef01791f3c35e28dcbd53f9352f0f81a6d361Jonathan Basseri * supported filter attributes are, 151449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <ul> 152449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <li>mcc: {@link CarrierIdentifier#getMcc}</li> 153449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <li>mnc: {@link CarrierIdentifier#getMnc}</li> 154449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <li>gid1: {@link CarrierIdentifier#getGid1}</li> 155449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <li>gid2: {@link CarrierIdentifier#getGid2}</li> 156449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <li>spn: {@link CarrierIdentifier#getSpn}</li> 157449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * <li>device: {@link Build.DEVICE}</li> 158449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * </ul> 159449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * </p> 160449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * 161449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * @param parser an XmlPullParser pointing at a START_TAG with the attributes to check. 162449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * @param id the carrier details to check against. 163449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri * @return false if any XML attribute does not match the corresponding value. 164449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri */ 165449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri static boolean checkFilters(XmlPullParser parser, CarrierIdentifier id) { 166449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri boolean result = true; 167449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri for (int i = 0; i < parser.getAttributeCount(); ++i) { 168449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri String attribute = parser.getAttributeName(i); 169449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri String value = parser.getAttributeValue(i); 170449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri switch (attribute) { 171449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri case "mcc": 172449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri result = result && value.equals(id.getMcc()); 173449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri break; 174449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri case "mnc": 175449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri result = result && value.equals(id.getMnc()); 176449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri break; 177449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri case "gid1": 1780665760d783c9ce7688359030ea2530e8c1ebb0aMeng Wang result = result && value.equalsIgnoreCase(id.getGid1()); 179449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri break; 180449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri case "gid2": 1810665760d783c9ce7688359030ea2530e8c1ebb0aMeng Wang result = result && value.equalsIgnoreCase(id.getGid2()); 182449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri break; 183449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri case "spn": 1840665760d783c9ce7688359030ea2530e8c1ebb0aMeng Wang result = result && value.equalsIgnoreCase(id.getSpn()); 185449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri break; 186449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri case "device": 1870665760d783c9ce7688359030ea2530e8c1ebb0aMeng Wang result = result && value.equalsIgnoreCase(Build.DEVICE); 188449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri break; 189449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri default: 190449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri Log.e(TAG, "Unknown attribute " + attribute + "=" + value); 191449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri result = false; 192449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri break; 193449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 194449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri } 195449236a9616591b07a9ff93f6888b89706ec9b37Jonathan Basseri return result; 1963a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri } 1973a4ccd212feb29a1c6c32b0e4e4cd9aae923ad1aJonathan Basseri} 198