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

Commit 80230055 authored by Tyler Gunn's avatar Tyler Gunn Committed by Android (Google) Code Review
Browse files

Merge "Add USSD codes over IMS feature" into rvc-dev

parents bbe9528f bf372b33
Loading
Loading
Loading
Loading
+18 −9
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.util.ArrayUtils;
import com.android.telephony.Rlog;
import com.android.internal.telephony.imsphone.ImsPhoneMmiCode;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -1280,9 +1281,6 @@ public class GsmCdmaPhone extends Phone {
        boolean allowWpsOverIms = configManager.getConfigForSubId(getSubId())
                .getBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL);

        boolean useImsForCall = useImsForCall(dialArgs)
                 && (isWpsCall ? allowWpsOverIms : true);

        boolean useImsForEmergency = imsPhone != null
                && isEmergency
                && alwaysTryImsForEmergencyCarrierConfig
@@ -1291,16 +1289,21 @@ public class GsmCdmaPhone extends Phone {

        String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
                stripSeparators(dialString));
        boolean isUt = (dialPart.startsWith("*") || dialPart.startsWith("#"))
        boolean isMmiCode = (dialPart.startsWith("*") || dialPart.startsWith("#"))
                && dialPart.endsWith("#");

        boolean isSuppServiceCode = ImsPhoneMmiCode.isSuppServiceCodes(dialPart, this);
        boolean isPotentialUssdCode = isMmiCode && !isSuppServiceCode;
        boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled();
        boolean useImsForCall = useImsForCall(dialArgs)
                && (isWpsCall ? allowWpsOverIms : true);

        if (DBG) {
            logd("useImsForCall=" + useImsForCall
                    + ", useImsForEmergency=" + useImsForEmergency
                    + ", useImsForUt=" + useImsForUt
                    + ", isUt=" + isUt
                    + ", isUt=" + isMmiCode
                    + ", isSuppServiceCode=" + isSuppServiceCode
                    + ", isPotentialUssdCode=" + isPotentialUssdCode
                    + ", isWpsCall=" + isWpsCall
                    + ", allowWpsOverIms=" + allowWpsOverIms
                    + ", imsPhone=" + imsPhone
@@ -1316,7 +1319,9 @@ public class GsmCdmaPhone extends Phone {

        Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mPhoneId, mContext);

        if ((useImsForCall && !isUt) || (isUt && useImsForUt) || useImsForEmergency) {
        if ((useImsForCall && (!isMmiCode || isPotentialUssdCode))
                || (isMmiCode && useImsForUt)
                || useImsForEmergency) {
            try {
                if (DBG) logd("Trying IMS PS call");
                return imsPhone.dial(dialString, dialArgs);
@@ -1345,7 +1350,9 @@ public class GsmCdmaPhone extends Phone {
        if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */
                && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
                && !isEmergency /* non-emergency call */
                && !(isUt && useImsForUt) /* not UT */) {
                && !(isMmiCode && useImsForUt) /* not UT */
                /* If config_allow_ussd_over_ims is false, USSD is sent over the CS pipe instead */
                && !isPotentialUssdCode) {
            throw new CallStateException(
                CallStateException.ERROR_POWER_OFF,
                "cannot dial voice call in airplane mode");
@@ -1358,7 +1365,9 @@ public class GsmCdmaPhone extends Phone {
                && ServiceState.isPsOnlyTech(
                        mSST.mSS.getRilDataRadioTechnology())) /* PS not in LTE/NR */
                && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
                && !isEmergency /* non-emergency call */) {
                && !isEmergency /* non-emergency call */
                /* If config_allow_ussd_over_ims is false, USSD is sent over the CS pipe instead */
                && !isPotentialUssdCode) {
            throw new CallStateException(
                CallStateException.ERROR_OUT_OF_SERVICE,
                "cannot dial voice call in out of service");
+79 −7
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.internal.telephony.imsphone;

import static android.telephony.ServiceState.STATE_IN_SERVICE;

import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
@@ -480,6 +482,60 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
        return false;
    }

    static boolean isPinPukCommand(String sc) {
        return sc != null && (sc.equals(SC_PIN) || sc.equals(SC_PIN2)
                || sc.equals(SC_PUK) || sc.equals(SC_PUK2));
    }

    /**
     * Whether the dial string is supplementary service code.
     *
     * @param dialString The dial string.
     * @return true if the dial string is supplementary service code, and {@code false} otherwise.
     */
    public static boolean isSuppServiceCodes(String dialString, Phone phone) {
        if (phone != null && phone.getServiceState().getVoiceRoaming()
                && phone.getDefaultPhone().supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming()) {
            /* The CDMA MMI coded dialString will be converted to a 3GPP MMI Coded dialString
               so that it can be processed by the matcher and code below
             */
            dialString = convertCdmaMmiCodesTo3gppMmiCodes(dialString);
        }

        Matcher m = sPatternSuppService.matcher(dialString);
        if (m.matches()) {
            String sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
            if (isServiceCodeCallForwarding(sc)) {
                return true;
            } else if (isServiceCodeCallBarring(sc)) {
                return true;
            } else if (sc != null && sc.equals(SC_CFUT)) {
                return true;
            } else if (sc != null && sc.equals(SC_CLIP)) {
                return true;
            } else if (sc != null && sc.equals(SC_CLIR)) {
                return true;
            } else if (sc != null && sc.equals(SC_COLP)) {
                return true;
            } else if (sc != null && sc.equals(SC_COLR)) {
                return true;
            } else if (sc != null && sc.equals(SC_CNAP)) {
                return true;
            } else if (sc != null && sc.equals(SC_BS_MT)) {
                return true;
            } else if (sc != null && sc.equals(SC_BAICa)) {
                return true;
            } else if (sc != null && sc.equals(SC_PWD)) {
                return true;
            } else if (sc != null && sc.equals(SC_WAIT)) {
                return true;
            } else if (isPinPukCommand(sc)) {
                return true;
            }
        }
        return false;
    }

    static String
    scToBarringFacility(String sc) {
        if (sc == null) {
@@ -645,8 +701,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
     * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related
     */
    public boolean isPinPukCommand() {
        return mSc != null && (mSc.equals(SC_PIN) || mSc.equals(SC_PIN2)
                              || mSc.equals(SC_PUK) || mSc.equals(SC_PUK2));
        return isPinPukCommand(mSc);
    }

    /**
@@ -1001,11 +1056,28 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
                    throw new RuntimeException ("Invalid or Unsupported MMI Code");
                }
            } else if (mPoundString != null) {
                // USSD codes are not supported over IMS due to modem limitations; send over the CS
                // pipe instead.  This should be fixed in the future.
                if (mContext.getResources().getBoolean(
                        com.android.internal.R.bool.config_allow_ussd_over_ims)) {
                    // We'll normally send USSD over the CS pipe, but if it happens that
                    // the CS phone is out of service, we'll just try over IMS instead.
                    if (mPhone.getDefaultPhone().getServiceStateTracker().mSS.getState()
                            == STATE_IN_SERVICE) {
                        Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
                                + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe "
                                + "(allowed over ims).");
                        throw new CallStateException(Phone.CS_FALLBACK);
                    } else {
                        Rlog.i(LOG_TAG, "processCode: CS is out of service, sending ussd string '"
                                + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe.");
                        sendUssd(mPoundString);
                    }
                } else {
                    // USSD codes are not supported over IMS due to modem limitations; send over
                    // the CS pipe instead.  This should be fixed in the future.
                    Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
                            + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe.");
                    throw new CallStateException(Phone.CS_FALLBACK);
                }
            } else {
                Rlog.d(LOG_TAG, "processCode: invalid or unsupported MMI");
                throw new RuntimeException ("Invalid or Unsupported MMI Code");
+73 −1
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@@ -49,6 +50,7 @@ import android.os.Message;
import android.os.Process;
import android.os.WorkSource;
import android.preference.PreferenceManager;
import android.telecom.VideoProfile;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.CellIdentity;
@@ -467,7 +469,7 @@ public class GsmCdmaPhoneTest extends TelephonyTest {

    @Test
    @SmallTest
    public void testDial() {
    public void testDial() throws Exception {
        try {
            mSST.mSS = mServiceState;
            doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
@@ -477,6 +479,8 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
            mCT.mRingingCall = mGsmCdmaCall;
            doReturn(GsmCdmaCall.State.IDLE).when(mGsmCdmaCall).getState();

            replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);

            Connection connection = mPhoneUT.dial("1234567890",
                    new PhoneInternalInterface.DialArgs.Builder().build());
            verify(mCT).dialGsm("1234567890", null, null);
@@ -1289,4 +1293,72 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
//        processAllMessages();
//        verify(mMockCi, never()).enableUiccApplications(eq(true), messageCaptor.capture());
//    }

    @Test
    @SmallTest
    public void testSendUssdInService() throws Exception {
        PhoneInternalInterface.DialArgs dialArgs = new PhoneInternalInterface.DialArgs
                .Builder().setVideoState(VideoProfile.STATE_AUDIO_ONLY).build();

        setupTestSendUssd(dialArgs);

        // ServiceState is in service.
        doReturn(ServiceState.STATE_IN_SERVICE).when(mSST.mSS).getState();
        mPhoneUT.dial("*135#", dialArgs);
        verify(mMockCi).sendUSSD(eq("*135#"), any());
    }

    @Test
    @SmallTest
    public void testSendUssdInOutOfService() throws Exception {
        PhoneInternalInterface.DialArgs dialArgs = new PhoneInternalInterface.DialArgs
                .Builder().setVideoState(VideoProfile.STATE_AUDIO_ONLY).build();

        setupTestSendUssd(dialArgs);

        // ServiceState is out of service
        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS)
                .getState(); /* CS out of service */
        doReturn(ServiceState.STATE_IN_SERVICE).when(mSST.mSS).getDataRegState();
        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_GSM).when(mSST.mSS)
                .getRilDataRadioTechnology(); /* PS not in LTE */
        mPhoneUT.dial("*135#", dialArgs);
        verify(mMockCi).sendUSSD(eq("*135#"), any());
    }

    @Test
    @SmallTest
    public void testSendUssdInAirplaneMode() throws Exception {
        PhoneInternalInterface.DialArgs dialArgs = new PhoneInternalInterface.DialArgs
                .Builder().setVideoState(VideoProfile.STATE_AUDIO_ONLY).build();

        setupTestSendUssd(dialArgs);

        // ServiceState is airplane mode.
        doReturn(ServiceState.STATE_POWER_OFF).when(mSST.mSS).getState(); /* CS POWER_OFF */
        mPhoneUT.dial("*135#", dialArgs);
        verify(mMockCi).sendUSSD(eq("*135#"), any());
    }

    private void setupTestSendUssd(PhoneInternalInterface.DialArgs dialArgs) throws Exception {
        mPhoneUT.mCi = mMockCi;
        ServiceState mImsServiceState = Mockito.mock(ServiceState.class);
        CallStateException callStateException = Mockito.mock(CallStateException.class);

        // Enable VoWiFi
        doReturn(true).when(mImsManager).isVolteEnabledByPlatform();
        doReturn(true).when(mImsManager).isEnhanced4gLteModeSettingEnabledByUser();
        doReturn(mImsServiceState).when(mImsPhone).getServiceState();
        doReturn(ServiceState.STATE_IN_SERVICE).when(mImsServiceState).getState();
        doReturn(true).when(mImsPhone).isWifiCallingEnabled();

        // Disable UT/XCAP
        doReturn(false).when(mImsPhone).isUtEnabled();

        // Throw CallStateException(Phone.CS_FALLBACK) from ImsPhone.dial().
        doReturn(Phone.CS_FALLBACK).when(callStateException).getMessage();
        doThrow(callStateException).when(mImsPhone).dial("*135#", dialArgs);

        replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
    }
}
+49 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.app.IApplicationThread;
import android.content.BroadcastReceiver;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
@@ -845,6 +846,54 @@ public class ImsPhoneTest extends TelephonyTest {
        assertNotNull(mImsPhoneUT.getServiceStateTracker());
    }

    @Test
    @SmallTest
    public void testSendUssdAllowUssdOverImsInOutOfService() throws Exception {
        Resources resources = mContext.getResources();

        doReturn(true).when(resources).getBoolean(
                com.android.internal.R.bool.config_allow_ussd_over_ims);
        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getState();

        mImsPhoneUT.dial("*135#", new ImsPhone.ImsDialArgs.Builder().build());
        verify(mImsCT).sendUSSD(eq("*135#"), any());
    }

    @Test
    @SmallTest
    public void testSendUssdAllowUssdOverImsInService() throws Exception {
        String errorCode = "";
        Resources resources = mContext.getResources();

        doReturn(true).when(resources).getBoolean(
                com.android.internal.R.bool.config_allow_ussd_over_ims);
        doReturn(ServiceState.STATE_IN_SERVICE).when(mSST.mSS).getState();

        try {
            mImsPhoneUT.dial("*135#", new ImsPhone.ImsDialArgs.Builder().build());
        } catch (CallStateException e) {
            errorCode = e.getMessage();
        }
        assertEquals(Phone.CS_FALLBACK, errorCode);
    }

    @Test
    @SmallTest
    public void testSendUssdNotAllowUssdOverIms() throws Exception {
        String errorCode = "";
        Resources resources = mContext.getResources();

        doReturn(false).when(resources).getBoolean(
                com.android.internal.R.bool.config_allow_ussd_over_ims);

        try {
            mImsPhoneUT.dial("*135#", new ImsPhone.ImsDialArgs.Builder().build());
        } catch (CallStateException e) {
            errorCode = e.getMessage();
        }
        assertEquals(Phone.CS_FALLBACK, errorCode);
    }

    private ServiceState getServiceStateDataAndVoice(int rat, int regState, boolean isRoaming) {
        ServiceState ss = new ServiceState();
        ss.setStateOutOfService();