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

Commit 5ec3a067 authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Add dial string validation for FAC codes.

FAC codes are formatted as:
#FAC#MSISDN*

The existing implementation just checked for a matching known FAC code
in carrier config and then just skipped processing it as a USSD/MMI and
dialed it as is.

It turns out in some regions, carriers have FAC codes that mean something
completely different than the same code dialed as a UUSD.

So, *#112*6505551212# and #112#6505551212* do something different 🙄.

To fix this, as part of the validation for a FAC code, we'll also verify
that the original dial string matches the expected format for an FAC code.
This ensures the aforementioned cases get handled as expected; in the
formed case as a USSD/MMI, and in the latter case as an FAC code.

Test: Added new unit test cases for this.
Test: Ran unit tests.
Fixes: 330528194
Change-Id: I74a680f31f6a9f4704d8cf80df397fca4cb01d37
parent bda9f8cd
Loading
Loading
Loading
Loading
+26 −2
Original line number Diff line number Diff line
@@ -200,6 +200,12 @@ public final class GsmMmiCode extends Handler implements MmiCode {
         10 = dialing number
*/

    /**
     * An FAC code is of the following format:
     * #FAC#MSISDN*
     */
    static Pattern sFac = Pattern.compile("^\\#\\d+\\#[^*]+\\*$");

    static final int MATCH_GROUP_POUND_STRING = 1;

    static final int MATCH_GROUP_ACTION = 2;
@@ -274,7 +280,9 @@ public final class GsmMmiCode extends Handler implements MmiCode {
                // in India operator(Mumbai MTNL)
                ret = new GsmMmiCode(phone, app);
                ret.mPoundString = dialString;
            } else if (ret.isFacToDial()) {
            } else if (ret.isFacToDial(dialString)) {
                // Note: we had to pass in the dial string above because the full dial string is not
                // in the MmiCode class (or even needed there).
                // This is a FAC (feature access code) to dial as a normal call.
                ret = null;
            }
@@ -962,13 +970,29 @@ public final class GsmMmiCode extends Handler implements MmiCode {
        }
    }

    /**
     * Determines if a full dial string matches the general format of a FAC code.
     * Ie. #FAC#MSIDN*
     * @param dialString The full dialed number.
     * @return {@code true} if the dialed number has the general format of a FAC code.
     */
    private static boolean isFacFormatNumber(String dialString) {
        Matcher m = sFac.matcher(dialString);
        return m.matches();
    }

    /**
     * Returns true if the Service Code is FAC to dial as a normal call.
     *
     * FAC stands for feature access code and it is special patterns of characters
     * to invoke certain features.
     */
    private boolean isFacToDial() {
    private boolean isFacToDial(String dialString) {
        if (!isFacFormatNumber(dialString)) {
            // If the full dial string doesn't conform to the required format for a FAC, we will
            // bail early. It is likely a true USSD which shares the same code as the FAC.
            return false;
        }
        CarrierConfigManager configManager = (CarrierConfigManager)
                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
        PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
+41 −0
Original line number Diff line number Diff line
@@ -20,12 +20,15 @@ import static com.google.common.truth.Truth.assertThat;

import static junit.framework.Assert.fail;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.verify;

import android.content.Context;
import android.os.AsyncResult;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
@@ -332,6 +335,44 @@ public class GsmMmiCodeTest extends TelephonyTest {
                eq(com.android.internal.R.string.mmiErrorNotSupported));
    }

    @Test
    public void testFacCode() {
        // Put a valid FAC code into the carrier config.
        CarrierConfigManager ccm = (CarrierConfigManager) mGsmCdmaPhoneUT.getContext()
                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
        PersistableBundle cc = ccm.getConfigForSubId(0);
        cc.putStringArray(CarrierConfigManager.KEY_FEATURE_ACCESS_CODES_STRING_ARRAY,
                new String[] {"112"});

        // Try using a dial string with the FAC; this should result in a NULL GsmMmiCode.
        GsmMmiCode validFac = GsmMmiCode.newFromDialString("#112#6505551212*", mGsmCdmaPhoneUT,
                null, null);
        assertNull(validFac);

        // Try using a dial string with the FAC; this should result in a NULL GsmMmiCode.
        // Note this case is somewhat contrived, however the GsmMmiCode parsing does allow non-digit
        // characters, so we will here too.
        GsmMmiCode validFac2 = GsmMmiCode.newFromDialString("#112#650-555-1212*", mGsmCdmaPhoneUT,
                null, null);
        assertNull(validFac2);

        // Try using a dial string with a different made up FAC; this one is not in the carrier
        // config so should just return an MMI code.
        GsmMmiCode invalidFac = GsmMmiCode.newFromDialString("#113#6505551212*", mGsmCdmaPhoneUT,
                null, null);
        assertNotNull(invalidFac);

        // Now try the carrier config FAC code, but it's formatted as if it a USSD.
        GsmMmiCode ussd = GsmMmiCode.newFromDialString("*#112*6505551212#", mGsmCdmaPhoneUT,
                null, null);
        assertNotNull(ussd);

        // Now try the carrier config FAC code, but it's not a valid FAC formatted string
        GsmMmiCode invalidFormat = GsmMmiCode.newFromDialString("*#112*6505551212", mGsmCdmaPhoneUT,
                null, null);
        assertNull(invalidFormat);
    }

    private void setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig() {
        final PersistableBundle bundle = new PersistableBundle();
        bundle.putBoolean(CarrierConfigManager