/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.carrierconfig;
import android.annotation.Nullable;
import android.os.Build;
import android.os.PersistableBundle;
import android.os.SystemProperties;
import android.service.carrier.CarrierIdentifier;
import android.service.carrier.CarrierService;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Provides network overrides for carrier configuration.
*
* The configuration available through CarrierConfigManager is a combination of default values,
* default network overrides, and carrier overrides. The default network overrides are provided by
* this service. For a given network, we look for a matching XML file in our assets folder, and
* return the PersistableBundle from that file. Assets are preferred over Resources because resource
* overlays only support using MCC+MNC and that doesn't work with MVNOs. The only resource file used
* is vendor.xml, to provide vendor-specific overrides.
*/
public class DefaultCarrierConfigService extends CarrierService {
private static final String SPN_EMPTY_MATCH = "null";
private static final String CARRIER_ID_PREFIX = "carrier_config_carrierid_";
private static final String MCCMNC_PREFIX = "carrier_config_mccmnc_";
private static final String TAG = "DefaultCarrierConfigService";
private XmlPullParserFactory mFactory;
public DefaultCarrierConfigService() {
Log.d(TAG, "Service created");
mFactory = null;
}
/**
* Returns per-network overrides for carrier configuration.
*
* This returns a carrier config bundle appropriate for the given carrier by reading data from
* files in our assets folder. Config files in assets folder are carrier-id-indexed
* {@link TelephonyManager#getSimCarrierId()}. NOTE: config files named after mccmnc
* are for those without a matching carrier id and should be renamed to carrier id once the
* missing IDs are added to
* carrier id list
*
* First, look for file named after
* carrier_config_carrierid_ This function iterates over each {@code Here is an example document in vendor.xml.
* {@code
*
Here is an example document. The second bundle will be applied to the first only if the * GID1 is ABCD. *
{@code ** * @param parser an XmlPullParser pointing at the beginning of the document. * @param id the details of the SIM operator used to filter parts of the document. If read from * files named after carrier id, this will be set to {@null code} as no filter match * needed. * @return a possibly empty PersistableBundle containing the config values. */ static PersistableBundle readConfigFromXml(XmlPullParser parser, @Nullable CarrierIdentifier id) throws IOException, XmlPullParserException { PersistableBundle config = new PersistableBundle(); if (parser == null) { return config; } // Iterate over each* * }* ** * **
This iterates over the attributes of the current tag pointed to by {@code parser} and * checks each one against {@code id} or {@link Build.DEVICE}. Attributes that are not specified * in the node will not be checked, so a node with no attributes will always return true. The * supported filter attributes are, *
* The attributes imsi and spn can be expressed as regexp to filter on patterns. * The spn attribute can be set to the string "null" to allow matching against a SIM * with no spn set. *
* * @param parser an XmlPullParser pointing at a START_TAG with the attributes to check. * @param id the carrier details to check against. * @return false if any XML attribute does not match the corresponding value. */ static boolean checkFilters(XmlPullParser parser, CarrierIdentifier id) { boolean result = true; String vendorSkuProperty = SystemProperties.get( "ro.boot.product.vendor.sku", ""); String hardwareSkuProperty = SystemProperties.get( "ro.boot.product.hardware.sku", ""); for (int i = 0; i < parser.getAttributeCount(); ++i) { String attribute = parser.getAttributeName(i); String value = parser.getAttributeValue(i); switch (attribute) { case "mcc": result = result && value.equals(id.getMcc()); break; case "mnc": result = result && value.equals(id.getMnc()); break; case "gid1": result = result && value.equalsIgnoreCase(id.getGid1()); break; case "gid2": result = result && value.equalsIgnoreCase(id.getGid2()); break; case "spn": result = result && matchOnSP(value, id); break; case "imsi": result = result && matchOnImsi(value, id); break; case "device": result = result && value.equalsIgnoreCase(Build.DEVICE); break; case "vendorSku": result = result && value.equalsIgnoreCase(vendorSkuProperty); break; case "hardwareSku": result = result && value.equalsIgnoreCase(hardwareSkuProperty); break; case "cid": result = result && ((Integer.parseInt(value) == id.getCarrierId()) || (Integer.parseInt(value) == id.getSpecificCarrierId())); break; case "name": // name is used together with cid for readability. ignore for filter. break; default: Log.e(TAG, "Unknown attribute " + attribute + "=" + value); result = false; break; } } return result; } /** * Check to see if the IMSI expression from the XML matches the IMSI of the * Carrier. * * @param xmlImsi IMSI expression fetched from the resource XML * @param id Id of the evaluated CarrierIdentifier * @return true if the XML IMSI matches the IMSI of CarrierIdentifier, false * otherwise. */ static boolean matchOnImsi(String xmlImsi, CarrierIdentifier id) { boolean matchFound = false; String currentImsi = id.getImsi(); // If we were able to retrieve current IMSI, see if it matches. if (currentImsi != null) { Pattern imsiPattern = Pattern.compile(xmlImsi, Pattern.CASE_INSENSITIVE); Matcher matcher = imsiPattern.matcher(currentImsi); matchFound = matcher.matches(); } return matchFound; } /** * Check to see if the service provider name expression from the XML matches the * CarrierIdentifier. * * @param xmlSP SP expression fetched from the resource XML * @param id Id of the evaluated CarrierIdentifier * @return true if the XML SP matches the phone's SP, false otherwise. */ static boolean matchOnSP(String xmlSP, CarrierIdentifier id) { boolean matchFound = false; String currentSP = id.getSpn(); if (SPN_EMPTY_MATCH.equalsIgnoreCase(xmlSP)) { if (TextUtils.isEmpty(currentSP)) { matchFound = true; } } else if (currentSP != null) { Pattern spPattern = Pattern.compile(xmlSP, Pattern.CASE_INSENSITIVE); Matcher matcher = spPattern.matcher(currentSP); matchFound = matcher.matches(); } return matchFound; } }