Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b0a1cbd authored by Jack Yu's avatar Jack Yu
Browse files

Excluded certain APNs (e.g. IMS) from mobile data usage.

Added not_metered capability to a mobile network if none
of its associated APN types are metered. Also used not_metered
capability to determine if a network should be accounted for
data usage or not instead of using network type, which is
always MOBILE after refactoring. Will add VT usage support
in next phase.

bug: 20888836
Change-Id: Ic39c9ffc84e27566a5b10ebf9aaa4ee3d6bd3e49
parent 102289f9
Loading
Loading
Loading
Loading
+79 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,9 @@


package com.android.internal.telephony.dataconnection;
package com.android.internal.telephony.dataconnection;


import android.content.Context;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.ServiceState;
import android.text.TextUtils;
import android.text.TextUtils;
@@ -26,6 +29,8 @@ import com.android.internal.telephony.uicc.IccRecords;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.List;
import java.util.Locale;
import java.util.Locale;


@@ -34,6 +39,10 @@ import java.util.Locale;
 */
 */
public class ApnSetting {
public class ApnSetting {


    static final String LOG_TAG = "ApnSetting";

    private static final boolean DBG = false;

    static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
    static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
    static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";
    static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";


@@ -103,6 +112,12 @@ public class ApnSetting {
     * */
     * */
    public boolean permanentFailed = false;
    public boolean permanentFailed = false;


    /**
     * Metered APN types which would be accounted for in data usage. This is a map of subId ->
     * set of metered apn strings for the carrier.
     */
    private static HashMap<Integer, HashSet<String>> sMeteredApnTypes = new HashMap<>();

    public ApnSetting(int id, String numeric, String carrier, String apn,
    public ApnSetting(int id, String numeric, String carrier, String apn,
            String proxy, String port,
            String proxy, String port,
            String mmsc, String mmsProxy, String mmsPort,
            String mmsc, String mmsProxy, String mmsPort,
@@ -389,6 +404,70 @@ public class ApnSetting {
        return false;
        return false;
    }
    }


    public boolean isMetered(Context context, int subId) {

        synchronized (sMeteredApnTypes) {
            HashSet<String> meteredApnSet = sMeteredApnTypes.get(subId);

            // In case of cache miss, we need to look up the settings from carrier config.
            if (meteredApnSet == null) {
                // Retrieve the metered APN types from carrier config
                CarrierConfigManager configManager = (CarrierConfigManager)
                        context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
                if (configManager == null) {
                    Rlog.e(LOG_TAG, "Carrier config service is not available");
                    return true;
                }

                PersistableBundle b = configManager.getConfig(subId);
                if (b == null) {
                    Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
                    return true;
                }

                String[] meteredApnTypes = b.getStringArray(
                        CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS);
                if (meteredApnTypes == null) {
                    Rlog.e(LOG_TAG, "KEY_CARRIER_METERED_APN_TYPES_STRINGS is not available. " +
                            "subId = " + subId);
                    return true;
                }

                meteredApnSet = new HashSet<String>(Arrays.asList(meteredApnTypes));
                sMeteredApnTypes.put(subId, meteredApnSet);
            }

            if (DBG) {
                Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are " +
                        Arrays.toString(meteredApnSet.toArray()));
            }

            // If all types of APN are metered, then this APN setting must be metered.
            if (meteredApnSet.contains(PhoneConstants.APN_TYPE_ALL)) {
                if (DBG) Rlog.d(LOG_TAG, "All APN types are metered.");
                return true;
            }

            for (String type : types) {
                // If one of the APN type is metered, then this APN setting is metered.
                if (meteredApnSet.contains(type)) {
                    if (DBG) Rlog.d(LOG_TAG, type + " is metered.");
                    return true;
                } else if (type.equals(PhoneConstants.APN_TYPE_ALL)) {
                    // Assuming no configuration error, if at least one APN type is
                    // metered, then this APN setting is metered.
                    if (meteredApnSet.size() > 0) {
                        if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered.");
                        return true;
                    }
                }
            }
        }

        if (DBG) Rlog.d(LOG_TAG, "Not metered. APN = " + toString());
        return false;
    }

    // TODO - if we have this function we should also have hashCode.
    // TODO - if we have this function we should also have hashCode.
    // Also should handle changes in type order and perhaps case-insensitivity
    // Also should handle changes in type order and perhaps case-insensitivity
    @Override
    @Override
+7 −0
Original line number Original line Diff line number Diff line
@@ -884,6 +884,13 @@ public class DataConnection extends StateMachine {
                    default:
                    default:
                }
                }
            }
            }

            // If none of the APN types associated with this APN setting is metered,
            // then we apply NOT_METERED capability to the network.
            if (!mApnSetting.isMetered(mPhone.getContext(), mPhone.getSubId())) {
                result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
            }

            result.maybeMarkCapabilitiesRestricted();
            result.maybeMarkCapabilitiesRestricted();
        }
        }
        int up = 14;
        int up = 14;
+1 −0
Original line number Original line Diff line number Diff line
@@ -467,6 +467,7 @@ public class ContextFixture implements TestFixture<Context> {
        }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt());
        }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt());


        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
        doReturn(mBundle).when(mCarrierConfigManager).getConfig(anyInt());


        mConfiguration.locale = Locale.US;
        mConfiguration.locale = Locale.US;
        doReturn(mConfiguration).when(mResources).getConfiguration();
        doReturn(mConfiguration).when(mResources).getConfiguration();
+199 −18
Original line number Original line Diff line number Diff line
@@ -16,25 +16,77 @@


package com.android.internal.telephony.dataconnection;
package com.android.internal.telephony.dataconnection;


import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.SmallTest;


import com.android.internal.telephony.dataconnection.ApnSetting;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyTest;


import junit.framework.TestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;


public class ApnSettingTest extends TestCase {
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;


    public static void assertApnSettingsEqual(List<ApnSetting> a1, List<ApnSetting> a2) {
public class ApnSettingTest extends TelephonyTest {

    private PersistableBundle mBundle;

    @Before
    public void setUp() throws Exception {
        super.setUp(getClass().getSimpleName());
        mBundle = mContextFixture.getCarrierConfigBundle();
    }

    @After
    public void tearDown() throws Exception {
        super.tearDown();
    }

    private ApnSetting createApnSetting(String[] apnTypes) {
        return new ApnSetting(
                2163,                   // id
                "44010",                // numeric
                "sp-mode",              // name
                "spmode.ne.jp",         // apn
                "",                     // proxy
                "",                     // port
                "",                     // mmsc
                "",                     // mmsproxy
                "",                     // mmsport
                "",                     // user
                "",                     // password
                -1,                     // authtype
                apnTypes,               // types
                "IP",                   // protocol
                "IP",                   // roaming_protocol
                true,                   // carrier_enabled
                0,                      // bearer
                0,                      // bearer_bitmask
                0,                      // profile_id
                false,                  // modem_cognitive
                0,                      // max_conns
                0,                      // wait_time
                0,                      // max_conns_time
                0,                      // mtu
                "",                     // mvno_type
                "");                    // mnvo_match_data
    }

    private static void assertApnSettingsEqual(List<ApnSetting> a1, List<ApnSetting> a2) {
        assertEquals(a1.size(), a2.size());
        assertEquals(a1.size(), a2.size());
        for (int i = 0; i < a1.size(); ++i) {
        for (int i = 0; i < a1.size(); ++i) {
            assertApnSettingEqual(a1.get(i), a2.get(i));
            assertApnSettingEqual(a1.get(i), a2.get(i));
        }
        }
    }
    }


    public static void assertApnSettingEqual(ApnSetting a1, ApnSetting a2) {
    private static void assertApnSettingEqual(ApnSetting a1, ApnSetting a2) {
        assertEquals(a1.carrier, a2.carrier);
        assertEquals(a1.carrier, a2.carrier);
        assertEquals(a1.apn, a2.apn);
        assertEquals(a1.apn, a2.apn);
        assertEquals(a1.proxy, a2.proxy);
        assertEquals(a1.proxy, a2.proxy);
@@ -66,6 +118,7 @@ public class ApnSettingTest extends TestCase {
        assertEquals(a1.mvnoMatchData, a2.mvnoMatchData);
        assertEquals(a1.mvnoMatchData, a2.mvnoMatchData);
    }
    }


    @Test
    @SmallTest
    @SmallTest
    public void testFromString() throws Exception {
    public void testFromString() throws Exception {
        String[] dunTypes = {"DUN"};
        String[] dunTypes = {"DUN"};
@@ -113,6 +166,7 @@ public class ApnSettingTest extends TestCase {
        assertEquals(null, ApnSetting.fromString(testString));
        assertEquals(null, ApnSetting.fromString(testString));
    }
    }


    @Test
    @SmallTest
    @SmallTest
    public void testArrayFromString() throws Exception {
    public void testArrayFromString() throws Exception {
        // Test a multiple v3 string.
        // Test a multiple v3 string.
@@ -135,6 +189,7 @@ public class ApnSettingTest extends TestCase {
        assertApnSettingsEqual(expectedApns, ApnSetting.arrayFromString(testString));
        assertApnSettingsEqual(expectedApns, ApnSetting.arrayFromString(testString));
    }
    }


    @Test
    @SmallTest
    @SmallTest
    public void testToString() throws Exception {
    public void testToString() throws Exception {
        String[] types = {"default", "*"};
        String[] types = {"default", "*"};
@@ -147,4 +202,130 @@ public class ApnSettingTest extends TestCase {
                "IPV6, IP, true, 14, 8192, 0, false, 0, 0, 0, 0, , , false";
                "IPV6, IP, true, 14, 8192, 0, false, 0, 0, 0, 0, , , false";
        assertEquals(expected, apn.toString());
        assertEquals(expected, apn.toString());
    }
    }

    @Test
    @SmallTest
    public void testIsMetered() throws Exception {
        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).
                isMetered(mContext, 1));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
                isMetered(mContext, 1));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_MMS}).
                isMetered(mContext, 1));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
                isMetered(mContext, 1));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
                isMetered(mContext, 1));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_ALL}).
                isMetered(mContext, 1));

        assertFalse(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
                isMetered(mContext, 1));

        assertFalse(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
                isMetered(mContext, 1));
    }

    @Test
    @SmallTest
    public void testIsMeteredAnother() throws Exception {
        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS}).
                isMetered(mContext, 2));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_SUPL}).
                isMetered(mContext, 2));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_CBS}).
                isMetered(mContext, 2));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
                isMetered(mContext, 2));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
                isMetered(mContext, 2));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_ALL}).
                isMetered(mContext, 2));

        assertFalse(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
                isMetered(mContext, 2));

        assertFalse(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_IMS}).
                isMetered(mContext, 2));

    }

    @Test
    @SmallTest
    public void testIsMeteredNothingCharged() throws Exception {
        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                new String[]{});

        assertFalse(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_IMS}).
                isMetered(mContext, 3));

        assertFalse(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS}).
                isMetered(mContext, 3));

        assertFalse(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA}).
                isMetered(mContext, 3));

        assertFalse(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_ALL}).
                isMetered(mContext, 3));
    }

    @Test
    @SmallTest
    public void testIsMeteredNothingFree() throws Exception {
        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                new String[]{PhoneConstants.APN_TYPE_ALL});

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_ALL}).
                isMetered(mContext, 4));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
                isMetered(mContext, 4));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
                isMetered(mContext, 4));

        assertTrue(createApnSetting(
                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
                isMetered(mContext, 4));

    }
}
}
 No newline at end of file