Add IMSI and SPN regexp filtering for MVNO support

The filtering criterias for selecting carrier configurations are
extended to cover IMSI. The reason for this to support MVNO
configuration for IMSI ranges (i.e. when the only way to
identify a MVNO is by the IMSI)

The xml arguments for IMSI and SPN can be designed as regular
expressions in order to add flexibility.

Some operators do not have SPN defined on there SIM card, but the
MVNOs in their network might have. This is indicated in the xml
by setting the spn argument to "null".

Bug: 32398239
Test: Verified on target with MVNO configs.
Change-Id: Ib4fd87fe484a50fd808fbe9843061ab894703531
diff --git a/assets/carrier_config_001001.xml b/assets/carrier_config_001001.xml
index bf8674a..985952c 100644
--- a/assets/carrier_config_001001.xml
+++ b/assets/carrier_config_001001.xml
@@ -1,6 +1,15 @@
 <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
 <carrier_config_list>
-<carrier_config>
-<boolean name="carrier_volte_available_bool" value="true" />
-</carrier_config>
+    <carrier_config>
+        <boolean name="carrier_volte_available_bool" value="true" />
+    </carrier_config>
+    <carrier_config spn="null">
+        <boolean name="carrier_volte_available_bool" value="true" />
+    </carrier_config>
+    <carrier_config spn="^Tes.*">
+        <boolean name="carrier_volte_available_bool" value="true" />
+    </carrier_config>
+    <carrier_config imsi="^001001.*">
+        <boolean name="carrier_volte_available_bool" value="true" />
+    </carrier_config>
 </carrier_config_list>
diff --git a/src/com/android/carrierconfig/DefaultCarrierConfigService.java b/src/com/android/carrierconfig/DefaultCarrierConfigService.java
index 9081821..c4c6431 100644
--- a/src/com/android/carrierconfig/DefaultCarrierConfigService.java
+++ b/src/com/android/carrierconfig/DefaultCarrierConfigService.java
@@ -7,6 +7,7 @@
 import android.service.carrier.CarrierService;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.Log;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -18,6 +19,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import com.android.internal.util.FastXmlSerializer;
 
@@ -33,6 +36,8 @@
  */
 public class DefaultCarrierConfigService extends CarrierService {
 
+    private static final String SPN_EMPTY_MATCH = "null";
+
     private static final String TAG = "DefaultCarrierConfigService";
 
     private XmlPullParserFactory mFactory;
@@ -154,10 +159,17 @@
      *   <li>gid1: {@link CarrierIdentifier#getGid1}</li>
      *   <li>gid2: {@link CarrierIdentifier#getGid2}</li>
      *   <li>spn: {@link CarrierIdentifier#getSpn}</li>
+     *   <li>imsi: {@link CarrierIdentifier#getImsi}</li>
      *   <li>device: {@link Build.DEVICE}</li>
      * </ul>
      * </p>
      *
+     * <p>
+     * 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.
+     * </p>
+     *
      * @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.
@@ -181,7 +193,10 @@
                     result = result && value.equals(id.getGid2());
                     break;
                 case "spn":
-                    result = result && value.equals(id.getSpn());
+                    result = result && matchOnSP(value, id);
+                    break;
+                case "imsi":
+                    result = result && matchOnImsi(value, id);
                     break;
                 case "device":
                     result = result && value.equals(Build.DEVICE);
@@ -194,4 +209,50 @@
         }
         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);
+            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);
+            Matcher matcher = spPattern.matcher(currentSP);
+            matchFound = matcher.matches();
+        }
+        return matchFound;
+    }
 }
diff --git a/tests/src/com/android/carrierconfig/CarrierConfigTest.java b/tests/src/com/android/carrierconfig/CarrierConfigTest.java
index 58802f3..fa33753 100644
--- a/tests/src/com/android/carrierconfig/CarrierConfigTest.java
+++ b/tests/src/com/android/carrierconfig/CarrierConfigTest.java
@@ -31,7 +31,7 @@
         forEachConfigXml(new ParserChecker() {
             public void check(XmlPullParser parser) throws XmlPullParserException, IOException {
                 PersistableBundle b = DefaultCarrierConfigService.readConfigFromXml(parser,
-                        new CarrierIdentifier("001", "001", "Test", "", "", ""));
+                        new CarrierIdentifier("001", "001", "Test", "001001123456789", "", ""));
                 assertNotNull("got null bundle", b);
             }
         });
@@ -56,6 +56,7 @@
                                 case "gid1":
                                 case "gid2":
                                 case "spn":
+                                case "imsi":
                                 case "device":
                                     break;
                                 default:
@@ -98,6 +99,7 @@
                                 // TODO: Check that the type is correct.
                                 break;
                             case "carrier_config_list":
+                            case "item":
                             case "carrier_config":
                                 // do nothing
                                 break;