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