Allow MVNO tethering APNS through gservices flags.
Shishir Agrawal [Mon, 25 Aug 2014 18:54:02 +0000 (11:54 -0700)]
The change allows multiple APNs to be specified in
Settings.Global.TETHER_DUN_APN. It also adds mvnoType, and mvnoMatchData
support to the ApnSetting class.

Bug: 12977533

Change-Id: I424fc405c1e6270bf55eadb130ed839d50575cf1

src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
src/java/com/android/internal/telephony/dataconnection/DcTracker.java
src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java

index a27711c..c759413 100755 (executable)
 
 package com.android.internal.telephony.dataconnection;
 
+import android.text.TextUtils;
+
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -66,13 +70,28 @@ public class ApnSetting {
     public final int waitTime;
     public final int maxConnsTime;
 
+    /**
+      * MVNO match type. Possible values:
+      *   "spn": Service provider name.
+      *   "imsi": IMSI.
+      *   "gid": Group identifier level 1.
+      */
+    public final String mvnoType;
+    /**
+      * MVNO data. Examples:
+      *   "spn": A MOBILE, BEN NL
+      *   "imsi": 302720x94, 2060188
+      *   "gid": 4E, 33
+      */
+    public final String mvnoMatchData;
+
     public ApnSetting(int id, String numeric, String carrier, String apn,
             String proxy, String port,
             String mmsc, String mmsProxy, String mmsPort,
             String user, String password, int authType, String[] types,
             String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
             int profileId, boolean modemCognitive, int maxConns, int waitTime, int maxConnsTime,
-            int mtu) {
+            int mtu, String mvnoType, String mvnoMatchData) {
         this.id = id;
         this.numeric = numeric;
         this.carrier = carrier;
@@ -99,6 +118,9 @@ public class ApnSetting {
         this.waitTime = waitTime;
         this.maxConnsTime = maxConnsTime;
         this.mtu = mtu;
+        this.mvnoType = mvnoType;
+        this.mvnoMatchData = mvnoMatchData;
+
     }
 
     /**
@@ -110,21 +132,21 @@ public class ApnSetting {
      * spaces are optional):
      *
      * v1 format:
-     *   <carrier>, <apn>, <proxy>, <port>, <mmsc>, <mmsproxy>,
-     *   <mmsport>, <user>, <password>, <authtype>, <mcc>,<mnc>,
-     *   <type>[, <type>...]
+     *   <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
+     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
+     *   <type>[| <type>...],
      *
      * v2 format:
-     *   [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <mmsc>, <mmsproxy>,
-     *   <mmsport>, <user>, <password>, <authtype>, <mcc>, <mnc>,
-     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>,
-     *   <bearer>
+     *   [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
+     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
+     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearer>,
      *
      * v3 format:
-     *   [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <mmsc>, <mmsproxy>,
-     *   <mmsport>, <user>, <password>, <authtype>, <mcc>, <mnc>,
+     *   [ApnSettingV3] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
+     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
      *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearer>,
-     *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>
+     *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
+     *   <mvnoType>, <mvnoMatchData>
      *
      * Note that the strings generated by toString() do not contain the username
      * and password and thus cannot be read by this method.
@@ -152,20 +174,22 @@ public class ApnSetting {
         int authType;
         try {
             authType = Integer.parseInt(a[12]);
-        } catch (Exception e) {
+        } catch (NumberFormatException e) {
             authType = 0;
         }
 
         String[] typeArray;
         String protocol, roamingProtocol;
         boolean carrierEnabled;
-        int bearer;
+        int bearer = 0;
         int profileId = 0;
         boolean modemCognitive = false;
         int maxConns = 0;
         int waitTime = 0;
         int maxConnsTime = 0;
         int mtu = PhoneConstants.UNSET_MTU;
+        String mvnoType = "";
+        String mvnoMatchData = "";
         if (version == 1) {
             typeArray = new String[a.length - 13];
             System.arraycopy(a, 13, typeArray, 0, a.length - 13);
@@ -180,39 +204,68 @@ public class ApnSetting {
             typeArray = a[13].split("\\s*\\|\\s*");
             protocol = a[14];
             roamingProtocol = a[15];
+            carrierEnabled = Boolean.parseBoolean(a[16]);
+
             try {
-                carrierEnabled = Boolean.parseBoolean(a[16]);
-            } catch (Exception e) {
-                carrierEnabled = true;
+                bearer = Integer.parseInt(a[17]);
+            } catch (NumberFormatException ex) {
             }
-            bearer = Integer.parseInt(a[17]);
+
             if (a.length > 22) {
+                modemCognitive = Boolean.parseBoolean(a[19]);
                 try {
                     profileId = Integer.parseInt(a[18]);
-                    modemCognitive = Boolean.parseBoolean(a[19]);
                     maxConns = Integer.parseInt(a[20]);
                     waitTime = Integer.parseInt(a[21]);
                     maxConnsTime = Integer.parseInt(a[22]);
-                } catch (Exception e) {
+                } catch (NumberFormatException e) {
                 }
             }
             if (a.length > 23) {
                 try {
                     mtu = Integer.parseInt(a[23]);
-                } catch (Exception e) {
+                } catch (NumberFormatException e) {
                 }
             }
+            if (a.length > 25) {
+                mvnoType = a[24];
+                mvnoMatchData = a[25];
+            }
         }
 
         return new ApnSetting(-1,a[10]+a[11],a[0],a[1],a[2],a[3],a[7],a[8],
                 a[9],a[4],a[5],authType,typeArray,protocol,roamingProtocol,carrierEnabled,bearer,
-                profileId, modemCognitive, maxConns, waitTime, maxConnsTime, mtu);
+                profileId, modemCognitive, maxConns, waitTime, maxConnsTime, mtu,
+                mvnoType, mvnoMatchData);
+    }
+
+    /**
+     * Creates an array of ApnSetting objects from a string.
+     *
+     * @param data the string to read.
+     *
+     * Builds on top of the same format used by fromString, but allows for multiple entries
+     * separated by "; ".
+     */
+    public static List<ApnSetting> arrayFromString(String data) {
+        List<ApnSetting> retVal = new ArrayList<ApnSetting>();
+        if (TextUtils.isEmpty(data)) {
+            return retVal;
+        }
+        String[] apnStrings = data.split("\\s*;\\s*");
+        for (String apnString : apnStrings) {
+            ApnSetting apn = fromString(apnString);
+            if (apn != null) {
+                retVal.add(apn);
+            }
+        }
+        return retVal;
     }
 
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("[ApnSettingV2] ")
+        sb.append("[ApnSettingV3] ")
         .append(carrier)
         .append(", ").append(id)
         .append(", ").append(numeric)
@@ -239,9 +292,18 @@ public class ApnSetting {
         sb.append(", ").append(waitTime);
         sb.append(", ").append(maxConnsTime);
         sb.append(", ").append(mtu);
+        sb.append(", ").append(mvnoType);
+        sb.append(", ").append(mvnoMatchData);
         return sb.toString();
     }
 
+    /**
+     * Returns true if there are MVNO params specified.
+     */
+    public boolean hasMvnoParams() {
+        return !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData);
+    }
+
     public boolean canHandleType(String type) {
         if (!carrierEnabled) return false;
         for (String t : types) {
index 08bf625..6a92948 100644 (file)
@@ -1075,22 +1075,23 @@ public final class DcTracker extends DcTrackerBase {
         return true;
     }
 
-    private boolean mvnoMatches(IccRecords r, String mvno_type, String mvno_match_data) {
-        if (mvno_type.equalsIgnoreCase("spn")) {
+    @Override
+    protected boolean mvnoMatches(IccRecords r, String mvnoType, String mvnoMatchData) {
+        if (mvnoType.equalsIgnoreCase("spn")) {
             if ((r.getServiceProviderName() != null) &&
-                    r.getServiceProviderName().equalsIgnoreCase(mvno_match_data)) {
+                    r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
                 return true;
             }
-        } else if (mvno_type.equalsIgnoreCase("imsi")) {
+        } else if (mvnoType.equalsIgnoreCase("imsi")) {
             String imsiSIM = r.getIMSI();
-            if ((imsiSIM != null) && imsiMatches(mvno_match_data, imsiSIM)) {
+            if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
                 return true;
             }
-        } else if (mvno_type.equalsIgnoreCase("gid")) {
+        } else if (mvnoType.equalsIgnoreCase("gid")) {
             String gid1 = r.getGid1();
-            int mvno_match_data_length = mvno_match_data.length();
+            int mvno_match_data_length = mvnoMatchData.length();
             if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
-                    gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvno_match_data)) {
+                    gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
                 return true;
             }
         }
@@ -1133,44 +1134,35 @@ public final class DcTracker extends DcTrackerBase {
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.WAIT_TIME)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS_TIME)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)));
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE)),
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA)));
         return apn;
     }
 
     private ArrayList<ApnSetting> createApnList(Cursor cursor) {
-        ArrayList<ApnSetting> result = new ArrayList<ApnSetting>();
+        ArrayList<ApnSetting> mnoApns = new ArrayList<ApnSetting>();
+        ArrayList<ApnSetting> mvnoApns = new ArrayList<ApnSetting>();
         IccRecords r = mIccRecords.get();
 
         if (cursor.moveToFirst()) {
-            String mvnoType = null;
-            String mvnoMatchData = null;
             do {
-                String cursorMvnoType = cursor.getString(
-                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE));
-                String cursorMvnoMatchData = cursor.getString(
-                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA));
-                if (mvnoType != null) {
-                    if (mvnoType.equals(cursorMvnoType) &&
-                            mvnoMatchData.equals(cursorMvnoMatchData)) {
-                        result.add(makeApnSetting(cursor));
+                ApnSetting apn = makeApnSetting(cursor);
+                if (apn == null) {
+                    continue;
+                }
+
+                if (apn.hasMvnoParams()) {
+                    if (r != null && mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
+                        mvnoApns.add(apn);
                     }
                 } else {
-                    // no mvno match yet
-                    if (mvnoMatches(r, cursorMvnoType, cursorMvnoMatchData)) {
-                        // first match - toss out non-mvno data
-                        result.clear();
-                        mvnoType = cursorMvnoType;
-                        mvnoMatchData = cursorMvnoMatchData;
-                        result.add(makeApnSetting(cursor));
-                    } else {
-                        // add only non-mvno data
-                        if (cursorMvnoType.equals("")) {
-                            result.add(makeApnSetting(cursor));
-                        }
-                    }
+                    mnoApns.add(apn);
                 }
             } while (cursor.moveToNext());
         }
+
+        ArrayList<ApnSetting> result = mvnoApns.isEmpty() ? mnoApns : mvnoApns;
         if (DBG) log("createApnList: X result=" + result);
         return result;
     }
index 8d0fd0a..7e0fd01 100644 (file)
@@ -68,6 +68,7 @@ import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -640,19 +641,26 @@ public abstract class DcTrackerBase extends Handler {
         Context c = mPhone.getContext();
         String apnData = Settings.Global.getString(c.getContentResolver(),
                 Settings.Global.TETHER_DUN_APN);
-        ApnSetting dunSetting = ApnSetting.fromString(apnData);
-        if (dunSetting != null) {
+        List<ApnSetting> dunSettings = ApnSetting.arrayFromString(apnData);
+        for (ApnSetting dunSetting : dunSettings) {
             IccRecords r = mIccRecords.get();
             String operator = (r != null) ? r.getOperatorNumeric() : "";
             if (dunSetting.numeric.equals(operator)) {
-                if (VDBG) log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
-                return dunSetting;
+                if (dunSetting.hasMvnoParams()) {
+                    if (r != null && mvnoMatches(r, dunSetting.mvnoType, dunSetting.mvnoMatchData)) {
+                        if (VDBG) log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
+                        return dunSetting;
+                    }
+                } else {
+                    if (VDBG) log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
+                    return dunSetting;
+                }
             }
         }
 
         apnData = c.getResources().getString(R.string.config_tether_apndata);
-        dunSetting = ApnSetting.fromString(apnData);
-        if (VDBG) log("fetchDunApn: config_tether_apndata dunSetting=" + dunSetting);
+        ApnSetting dunSetting = ApnSetting.fromString(apnData);
+        if (VDBG) log("fetchDunApn: config_tether_apndata dunSetting=" + dunSettings);
         return dunSetting;
     }
 
@@ -750,6 +758,7 @@ public abstract class DcTrackerBase extends Handler {
     public abstract void setDataAllowed(boolean enable, Message response);
     public abstract String[] getPcscfAddress(String apnType);
     public abstract void setImsRegistrationState(boolean registered);
+    protected abstract boolean mvnoMatches(IccRecords r, String mvno_type, String mvno_match_data);
 
     @Override
     public void handleMessage(Message msg) {
index 4a4a07a..6e40760 100755 (executable)
 package com.android.internal.telephony;
 
 import android.test.suitebuilder.annotation.SmallTest;
+import android.test.MoreAsserts;
 
 import com.android.internal.telephony.dataconnection.ApnSetting;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import junit.framework.TestCase;
 
 public class ApnSettingTest extends TestCase {
 
     public static final String[] TYPES = {"default", "*"};
 
+    public static void assertApnSettingsEqual(List<ApnSetting> a1, List<ApnSetting> a2) {
+        assertEquals(a1.size(), a2.size());
+        for (int i = 0; i < a1.size(); ++i) {
+            assertApnSettingEqual(a1.get(i), a2.get(i));
+        }
+    }
+
     public static void assertApnSettingEqual(ApnSetting a1, ApnSetting a2) {
         assertEquals(a1.carrier,  a2.carrier);
         assertEquals(a1.apn,      a2.apn);
@@ -48,6 +59,14 @@ public class ApnSettingTest extends TestCase {
         }
         assertEquals(a1.carrierEnabled, a2.carrierEnabled);
         assertEquals(a1.bearer, a2.bearer);
+        assertEquals(a1.profileId, a2.profileId);
+        assertEquals(a1.modemCognitive, a2.modemCognitive);
+        assertEquals(a1.maxConns, a2.maxConns);
+        assertEquals(a1.waitTime, a2.waitTime);
+        assertEquals(a1.maxConnsTime, a2.maxConnsTime);
+        assertEquals(a1.mtu, a2.mtu);
+        assertEquals(a1.mvnoType, a2.mvnoType);
+        assertEquals(a1.mvnoMatchData, a2.mvnoMatchData);
     }
 
     @SmallTest
@@ -55,31 +74,41 @@ public class ApnSettingTest extends TestCase {
         String[] dunTypes = {"DUN"};
         String[] mmsTypes = {"mms", "*"};
 
-        ApnSetting expected_apn;
+        ApnSetting expectedApn;
         String testString;
 
         // A real-world v1 example string.
         testString = "Vodafone IT,web.omnitel.it,,,,,,,,,222,10,,DUN";
-        expected_apn =  new ApnSetting(
+        expectedApn = new ApnSetting(
                 -1, "22210", "Vodafone IT", "web.omnitel.it", "", "",
-                "", "", "", "", "", 0, dunTypes, "IP", "IP",true,0);
-        assertApnSettingEqual(expected_apn, ApnSetting.fromString(testString));
+                "", "", "", "", "", 0, dunTypes, "IP", "IP",true, 0,
+                0, false, 0, 0, 0, 0, "", "");
+        assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v2 string.
         testString = "[ApnSettingV2] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14";
-        expected_apn =  new ApnSetting(
+        expectedApn = new ApnSetting(
                 -1, "12345", "Name", "apn", "", "",
-                "", "", "", "", "", 0, mmsTypes, "IPV6", "IP",true,14);
-        assertApnSettingEqual(expected_apn, ApnSetting.fromString(testString));
+                "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14,
+                0, false, 0, 0, 0, 0, "", "");
+        assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // A v2 string with spaces.
         testString = "[ApnSettingV2] Name,apn, ,,,,,,,,123,45,,mms|*,IPV4V6, IP,true,14";
-        expected_apn =  new ApnSetting(
+        expectedApn = new ApnSetting(
                 -1, "12345", "Name", "apn", "", "",
-                "", "", "", "", "", 0, mmsTypes, "IPV4V6", "IP",true,14);
-        assertApnSettingEqual(expected_apn, ApnSetting.fromString(testString));
-
-        // Return null if insufficient fields given.
+                "", "", "", "", "", 0, mmsTypes, "IPV4V6", "IP", true, 14,
+                0, false, 0, 0, 0, 0, "", "");
+        assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+        // A v3 string.
+        testString = "[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14,,,,,,,spn,testspn";
+        expectedApn = new ApnSetting(
+                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV4V6",
+                "IP",true, 14, 0, false, 0, 0, 0, 0, "spn", "testSpn");
+        assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+        // Return no apn if insufficient fields given.
         testString = "[ApnSettingV2] Name,apn,,,,,,,,,123, 45,,mms|*";
         assertEquals(null, ApnSetting.fromString(testString));
 
@@ -87,6 +116,24 @@ public class ApnSettingTest extends TestCase {
         assertEquals(null, ApnSetting.fromString(testString));
     }
 
+    @SmallTest
+    public void testArrayFromString() throws Exception {
+        // Test a multiple v3 string.
+        String testString = "[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14,,,,,,,spn,testspn";
+        testString += " ;[ApnSettingV3] Name1,apn1,,,,,,,,,123,46,,mms|*,IPV6,IP,true,12,,,,,,,gid,testGid";
+        testString += " ;[ApnSettingV3] Name1,apn2,,,,,,,,,123,46,,mms|*,IPV6,IP,true,12,,,,,,,,";
+        List<ApnSetting> expectedApns = new ArrayList<ApnSetting>();
+        expectedApns.add(new ApnSetting(
+                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV4V6",
+                "IP",true, 14, 0, false, 0, 0, 0, 0, "spn", "testSpn"));
+        expectedApns.add(new ApnSetting(
+                -1, "12346", "Name1", "apn1", "", "", "", "", "", "", "", 0, mmsTypes, "IPV4V6",
+                "IP",true, 12, 0, false, 0, 0, 0, 0, "gid", "testGid"));
+        expectedApns.add(new ApnSetting(
+                -1, "12346", "Name1", "apn2", "", "", "", "", "", "", "", 0, mmsTypes, "IPV4V6",
+                "IP",true, 12, 0, false, 0, 0, 0, 0, "", ""));
+        assertApnSettingsEqual(expectedApns, ApnSetting.arrayFromString(testString));
+    }
 
     @SmallTest
     public void testToString() throws Exception {
@@ -94,10 +141,10 @@ public class ApnSettingTest extends TestCase {
         ApnSetting apn =  new ApnSetting(
                 99, "12345", "Name", "apn", "proxy", "port",
                 "mmsc", "mmsproxy", "mmsport", "user", "password", 0,
-                types, "IPV4V6", "IP", true, 14);
+                types, "IPV4V6", "IP", true, 14, 0, false, 0, 0, 0, 0, "", "");
         String expected = "[ApnSettingV2] Name, 99, 12345, apn, proxy, " +
                 "mmsc, mmsproxy, mmsport, port, 0, default | *, " +
-                "IPV4V6, IP, true, 14";
+                "IPV4V6, IP, true, 14, 0, false, 0, 0, 0, 0, , ";
         assertEquals(expected, apn.toString());
     }
 }