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

Commit 22bcfbed authored by Brad Ebinger's avatar Brad Ebinger
Browse files

Ignore IWLAN RAT roaming mode in legacy mode

Certain Wifi Calling configurations for home and
roaming mode were causing devices to move between
IWLAN and cellular data registration repeatedly.
Instead, we should not change the roaming mode if
voice is not registered and data moves from LTE->
IWLAN.

Bug: 129114951
Test: atest FrameworksTelephonyTests:ImsPhoneTest
Change-Id: Ic3628e04e4cbcd6d51489a4c3081efed76de6758
parent 6a71c891
Loading
Loading
Loading
Loading
+72 −32
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import android.os.RegistrantList;
import android.os.ResultReceiver;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneNumberUtils;
@@ -92,9 +93,11 @@ import com.android.internal.telephony.MmiCode;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneNotifier;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyComponentFactory;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.gsm.GsmMmiCode;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.uicc.IccRecords;
@@ -120,7 +123,8 @@ public class ImsPhone extends ImsPhoneBase {
    private static final int EVENT_SET_CLIR_DONE                     = EVENT_LAST + 5;
    private static final int EVENT_GET_CLIR_DONE                     = EVENT_LAST + 6;
    private static final int EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED  = EVENT_LAST + 7;
    private static final int EVENT_SERVICE_STATE_CHANGED             = EVENT_LAST + 8;
    @VisibleForTesting
    public static final int EVENT_SERVICE_STATE_CHANGED             = EVENT_LAST + 8;
    private static final int EVENT_VOICE_CALL_ENDED                  = EVENT_LAST + 9;

    static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
@@ -1497,25 +1501,16 @@ public class ImsPhone extends ImsPhoneBase {
                if (VDBG) logd("EVENT_SERVICE_STATE_CHANGED");
                ar = (AsyncResult) msg.obj;
                ServiceState newServiceState = (ServiceState) ar.result;
                // only update if roaming status changed
                if (mRoaming != newServiceState.getRoaming()) {
                    if (DBG) logd("Roaming state changed - " + mRoaming);
                    // Update WFC mode only if voice or data is in service.
                    // The STATE_IN_SERVICE is checked to prevent wifi calling mode change
                    // when phone moves from roaming to no service.
                    boolean isInService =
                            (newServiceState.getVoiceRegState() == ServiceState.STATE_IN_SERVICE ||
                            newServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE);
                    updateRoamingState(newServiceState.getRoaming(), isInService);
                }
                updateRoamingState(newServiceState);
                break;
            case EVENT_VOICE_CALL_ENDED:
                if (DBG) logd("Voice call ended. Handle pending updateRoamingState.");
                mCT.unregisterForVoiceCallEnded(this);
                // only update if roaming status changed
                boolean newRoaming = getCurrentRoaming();
                if (mRoaming != newRoaming) {
                    updateRoamingState(newRoaming, true);
                // Get the current unmodified ServiceState from the tracker, as it has more info
                // about the cell roaming state.
                ServiceStateTracker sst = getDefaultPhone().getServiceStateTracker();
                if (sst != null) {
                    updateRoamingState(sst.mSS);
                }
                break;

@@ -1888,27 +1883,72 @@ public class ImsPhone extends ImsPhoneBase {
        return mCT.getVtDataUsage(perUidStats);
    }

    private void updateRoamingState(boolean newRoaming, boolean isInService) {
    /**
     * Update roaming state and WFC mode in the following situations:
     *     1) voice is in service.
     *     2) data is in service and it is not IWLAN (if in legacy mode).
     * @param ss non-null ServiceState
     */
    private void updateRoamingState(ServiceState ss) {
        if (ss == null) {
            loge("updateRoamingState: null ServiceState!");
            return;
        }
        boolean newRoamingState = ss.getRoaming();
        // Do not recalculate if there is no change to state.
        if (mRoaming == newRoamingState) {
            return;
        }
        boolean isInService = (ss.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
                || ss.getDataRegState() == ServiceState.STATE_IN_SERVICE);
        // If we are not IN_SERVICE for voice or data, ignore change roaming state, as we always
        // move to home in this case.
        if (!isInService) {
            logi("updateRoamingState: we are OUT_OF_SERVICE, ignoring roaming change.");
            return;
        }
        // We ignore roaming changes when moving to IWLAN because it always sets the roaming
        // mode to home and masks the actual cellular roaming status if voice is not registered. If
        // we just moved to IWLAN because WFC roaming mode is IWLAN preferred and WFC home mode is
        // cell preferred, we can get into a condition where the modem keeps bouncing between
        // IWLAN->cell->IWLAN->cell...
        if (isCsNotInServiceAndPsWwanReportingWlan(ss)) {
            logi("updateRoamingState: IWLAN masking roaming, ignore roaming change.");
            return;
        }
        if (mCT.getState() == PhoneConstants.State.IDLE) {
            if (DBG) logd("updateRoamingState now: " + newRoaming);
            mRoaming = newRoaming;
            if (isInService) {
            if (DBG) logd("updateRoamingState now: " + newRoamingState);
            mRoaming = newRoamingState;
            ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
                imsManager.setWfcMode(imsManager.getWfcMode(newRoaming), newRoaming);
            imsManager.setWfcMode(imsManager.getWfcMode(newRoamingState), newRoamingState);
        } else {
                if (DBG) Rlog.d(LOG_TAG, "updateRoamingState service state is OUT_OF_SERVICE");
            }
        } else {
            if (DBG) logd("updateRoamingState postponed: " + newRoaming);
            mCT.registerForVoiceCallEnded(this,
                    EVENT_VOICE_CALL_ENDED, null);
            if (DBG) logd("updateRoamingState postponed: " + newRoamingState);
            mCT.registerForVoiceCallEnded(this, EVENT_VOICE_CALL_ENDED, null);
        }
    }

    private boolean getCurrentRoaming() {
        TelephonyManager tm = (TelephonyManager) mContext
                .getSystemService(Context.TELEPHONY_SERVICE);
        return tm.isNetworkRoaming(getSubId());
    /**
     * In legacy mode, data registration will report IWLAN when we are using WLAN for data,
     * effectively masking the true roaming state of the device if voice is not registered.
     *
     * @return true if we are reporting not in service for CS domain over WWAN transport and WLAN
     * for PS domain over WWAN transport.
     */
    private boolean isCsNotInServiceAndPsWwanReportingWlan(ServiceState ss) {
        TransportManager tm = mDefaultPhone.getTransportManager();
        // We can not get into this condition if we are in AP-Assisted mode.
        if (tm == null || !tm.isInLegacyMode()) {
            return false;
        }
        NetworkRegistrationInfo csInfo = ss.getNetworkRegistrationInfo(
                NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
        NetworkRegistrationInfo psInfo = ss.getNetworkRegistrationInfo(
                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
        // We will return roaming state correctly if the CS domain is in service because
        // ss.getRoaming() returns isVoiceRoaming||isDataRoaming result and isDataRoaming==false
        // when the modem reports IWLAN RAT.
        return psInfo != null && csInfo != null && !csInfo.isInService()
                && psInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_IWLAN;
    }

    @Override
+164 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyChar;
import static org.mockito.Matchers.anyInt;
@@ -643,4 +644,167 @@ public class ImsPhoneTest extends TelephonyTest {
        assertEquals(messageNotification,
                intent.getValue().getStringExtra(Phone.EXTRA_KEY_NOTIFICATION_MESSAGE));
    }

    @Test
    @SmallTest
    public void testRoamingDuplicateMessages() throws Exception {
        doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();

        //roaming - data registration only on LTE
        Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
                ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, true));
        // Inject the message synchronously instead of waiting for the thread to do it.
        mImsPhoneUT.handleMessage(m);

        verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(true));

        // Send a duplicate message
        mImsPhoneUT.handleMessage(m);
        m.recycle();

        // setWfcMode should not be called again.
        verify(mImsManager, times(1)).setWfcMode(anyInt(), anyBoolean());
    }


    @Test
    @SmallTest
    public void testRoamingToOutOfService() throws Exception {
        doReturn(true).when(mTransportManager).isInLegacyMode();
        doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();

        //roaming - data registration only on LTE
        Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
                ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, true));
        // Inject the message synchronously instead of waiting for the thread to do it.
        mImsPhoneUT.handleMessage(m);
        m.recycle();

        verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(true));

        // move to out of service
        m = getServiceStateChangedMessage(getServiceStateOutOfService());
        mImsPhoneUT.handleMessage(m);
        m.recycle();

        // setWfcMode should not be called again, out_of_service should not trigger move out of
        // roaming.
        verify(mImsManager, times(1)).setWfcMode(anyInt(), anyBoolean());
    }

    @Test
    @SmallTest
    public void testRoamingChangeForLteInLegacyMode() throws Exception {
        doReturn(true).when(mTransportManager).isInLegacyMode();
        doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();

        //roaming - data registration only on LTE
        Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
                ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, true));
        // Inject the message synchronously instead of waiting for the thread to do it.
        mImsPhoneUT.handleMessage(m);
        m.recycle();

        verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(true));

        // not roaming - data registration on LTE
        m = getServiceStateChangedMessage(getServiceStateDataOnly(
                ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, false));
        mImsPhoneUT.handleMessage(m);
        m.recycle();

        verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(false));
    }

    @Test
    @SmallTest
    public void testDataOnlyRoamingCellToIWlanInLegacyMode() throws Exception {
        doReturn(true).when(mTransportManager).isInLegacyMode();
        doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();

        //roaming - data registration only on LTE
        Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
                ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, true));
        // Inject the message synchronously instead of waiting for the thread to do it.
        mImsPhoneUT.handleMessage(m);
        m.recycle();

        verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(true));

        // not roaming - data registration onto IWLAN
        m = getServiceStateChangedMessage(getServiceStateDataOnly(
                ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, ServiceState.STATE_IN_SERVICE, false));
        mImsPhoneUT.handleMessage(m);
        m.recycle();

        // Verify that it hasn't been called again.
        verify(mImsManager, times(1)).setWfcMode(anyInt(), anyBoolean());
    }

    @Test
    @SmallTest
    public void testCellVoiceDataChangeToWlanInLegacyMode() throws Exception {
        doReturn(true).when(mTransportManager).isInLegacyMode();
        doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();

        //roaming - voice/data registration on LTE
        ServiceState ss = getServiceStateDataAndVoice(
                ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, true);
        Message m = getServiceStateChangedMessage(ss);
        // Inject the message synchronously instead of waiting for the thread to do it.
        mImsPhoneUT.handleMessage(m);

        verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(true));

        // roaming - voice LTE, data registration onto IWLAN
        modifyServiceStateData(ss, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
                ServiceState.STATE_IN_SERVICE, false);
        mImsPhoneUT.handleMessage(m);
        m.recycle();

        // Verify that it hasn't been called again.
        verify(mImsManager, times(1)).setWfcMode(anyInt(), anyBoolean());
    }

    private ServiceState getServiceStateDataAndVoice(int rat, int regState, boolean isRoaming) {
        ServiceState ss = new ServiceState();
        ss.setStateOutOfService();
        ss.setDataRegState(regState);
        ss.setDataRoaming(isRoaming);
        ss.setRilDataRadioTechnology(rat);
        ss.setVoiceRegState(regState);
        ss.setVoiceRoaming(isRoaming);
        ss.setRilVoiceRadioTechnology(rat);
        return ss;
    }

    private ServiceState getServiceStateDataOnly(int rat, int regState, boolean isRoaming) {
        ServiceState ss = new ServiceState();
        ss.setStateOutOfService();
        ss.setDataRegState(regState);
        ss.setDataRoaming(isRoaming);
        ss.setRilDataRadioTechnology(rat);
        return ss;
    }

    private ServiceState modifyServiceStateData(ServiceState ss, int rat, int regState,
            boolean isRoaming) {
        ss.setStateOutOfService();
        ss.setDataRegState(regState);
        ss.setDataRoaming(isRoaming);
        ss.setRilDataRadioTechnology(rat);
        return ss;
    }

    private ServiceState getServiceStateOutOfService() {
        ServiceState ss = new ServiceState();
        ss.setStateOutOfService();
        return ss;
    }

    private Message getServiceStateChangedMessage(ServiceState ss) {
        Message m = Message.obtain(mImsPhoneUT.getHandler(), ImsPhone.EVENT_SERVICE_STATE_CHANGED);
        m.obj = AsyncResult.forMessage(m, ss, null);
        return m;
    }
}