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 Diff line number Diff line
@@ -16,6 +16,9 @@

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.ServiceState;
import android.text.TextUtils;
@@ -26,6 +29,8 @@ import com.android.internal.telephony.uicc.IccRecords;

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

@@ -34,6 +39,10 @@ import java.util.Locale;
 */
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 V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";

@@ -103,6 +112,12 @@ public class ApnSetting {
     * */
    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,
            String proxy, String port,
            String mmsc, String mmsProxy, String mmsPort,
@@ -389,6 +404,70 @@ public class ApnSetting {
        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.
    // Also should handle changes in type order and perhaps case-insensitivity
    @Override
+7 −0
Original line number Diff line number Diff line
@@ -884,6 +884,13 @@ public class DataConnection extends StateMachine {
                    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();
        }
        int up = 14;
+1 −0
Original line number Diff line number Diff line
@@ -467,6 +467,7 @@ public class ContextFixture implements TestFixture<Context> {
        }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt());

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

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

package com.android.internal.telephony.dataconnection;

import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
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.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());
        for (int i = 0; i < a1.size(); ++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.apn, a2.apn);
        assertEquals(a1.proxy, a2.proxy);
@@ -66,6 +118,7 @@ public class ApnSettingTest extends TestCase {
        assertEquals(a1.mvnoMatchData, a2.mvnoMatchData);
    }

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

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

    @Test
    @SmallTest
    public void testToString() throws Exception {
        String[] types = {"default", "*"};
@@ -147,4 +202,130 @@ public class ApnSettingTest extends TestCase {
                "IPV6, IP, true, 14, 8192, 0, false, 0, 0, 0, 0, , , false";
        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