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

Commit 2c46f309 authored by joonhunshin's avatar joonhunshin Committed by Joonhun Shin
Browse files

Change the method of obtaining IMS registration radio tech and add test case

PhoneSwitcher tries to register a callback to obtain IMS registration
radio tech. But this callback will be removed when the binding with
ImsService is changed. This is a timing issue caused by operating in a separate thread.
To avoid issue, using ImsPhone#registerForImsRegistrationChanges() to receive IMS registration radio tech instead of registring a callback into ImsManager.

Bug: 330120237
Bug: 333300524
Test: atest PhoneSwitcherTest
Test: manual test and verify based on text log
      after IMS registered -> wifi on -> APM on -> APM off -> wifi off
Change-Id: Ia1220faea739bce58e1c54889341e8dba899302b
parent 5a63b830
Loading
Loading
Loading
Loading
+89 −8
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Log;
import android.util.SparseIntArray;

import com.android.ims.ImsException;
import com.android.ims.ImsManager;
@@ -84,6 +85,7 @@ import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwitch;
@@ -321,7 +323,8 @@ public class PhoneSwitcher extends Handler {

    private ConnectivityManager mConnectivityManager;
    private int mImsRegistrationTech = REGISTRATION_TECH_NONE;

    @VisibleForTesting
    public final SparseIntArray mImsRegistrationRadioTechMap = new SparseIntArray();
    private List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;

    /** Data settings manager callback. Key is the phone id. */
@@ -515,6 +518,14 @@ public class PhoneSwitcher extends Handler {
                if (phone.getImsPhone() != null) {
                    phone.getImsPhone().registerForPreciseCallStateChanged(
                            this, EVENT_PRECISE_CALL_STATE_CHANGED, null);
                    if (mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                        // Initialize IMS registration tech
                        mImsRegistrationRadioTechMap.put(phoneId, REGISTRATION_TECH_NONE);
                        ((ImsPhone) phone.getImsPhone()).registerForImsRegistrationChanges(
                                this, EVENT_IMS_RADIO_TECH_CHANGED, null);

                        log("register handler to receive IMS registration : " + phoneId);
                    }
                }
                mDataSettingsManagerCallbacks.computeIfAbsent(phoneId,
                        v -> new DataSettingsManagerCallback(this::post) {
@@ -532,8 +543,11 @@ public class PhoneSwitcher extends Handler {
                            }});
                phone.getDataSettingsManager().registerCallback(
                        mDataSettingsManagerCallbacks.get(phoneId));

                if (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                    registerForImsRadioTechChange(context, phoneId);
                }
            }
            Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
            mCurrentDdsSwitchFailure.add(ddsFailure);
        }
@@ -742,7 +756,18 @@ public class PhoneSwitcher extends Handler {
            case EVENT_IMS_RADIO_TECH_CHANGED: {
                // register for radio tech change to listen to radio tech handover in case previous
                // attempt was not successful
                if (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                    registerForImsRadioTechChange();
                } else {
                    if (msg.obj == null) {
                        log("EVENT_IMS_RADIO_TECH_CHANGED but parameter is not available");
                        break;
                    }
                    if (!onImsRadioTechChanged((AsyncResult) (msg.obj))) {
                        break;
                    }
                }

                // if voice call state changes or in voice call didn't change
                // but RAT changes(e.g. Iwlan -> cross sim), reevaluate for data switch.
                if (updatesIfPhoneInVoiceCallChanged() || isAnyVoiceCallActiveOnDevice()) {
@@ -754,7 +779,9 @@ public class PhoneSwitcher extends Handler {
            case EVENT_PRECISE_CALL_STATE_CHANGED: {
                // register for radio tech change to listen to radio tech handover in case previous
                // attempt was not successful
                if (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                    registerForImsRadioTechChange();
                }

                // If the phoneId in voice call didn't change, do nothing.
                if (!updatesIfPhoneInVoiceCallChanged()) {
@@ -906,6 +933,45 @@ public class PhoneSwitcher extends Handler {
        }
    }

    /**
     * Only provide service for the handler of PhoneSwitcher.
     * @return true if the radio tech changed, otherwise false
     */
    private boolean onImsRadioTechChanged(@NonNull AsyncResult asyncResult) {
        ImsPhone.ImsRegistrationRadioTechInfo imsRegistrationRadioTechInfo =
                (ImsPhone.ImsRegistrationRadioTechInfo) asyncResult.result;
        if (imsRegistrationRadioTechInfo == null
                || imsRegistrationRadioTechInfo.phoneId() == INVALID_PHONE_INDEX
                || imsRegistrationRadioTechInfo.imsRegistrationState()
                == RegistrationManager.REGISTRATION_STATE_REGISTERING) {
            // Ignore REGISTERING state, handle only REGISTERED and NOT_REGISTERED
            log("onImsRadioTechChanged : result is not available");
            return false;
        }

        int phoneId = imsRegistrationRadioTechInfo.phoneId();
        int subId = SubscriptionManager.getSubscriptionId(phoneId);
        int tech = imsRegistrationRadioTechInfo.imsRegistrationTech();
        log("onImsRadioTechChanged phoneId : " + phoneId + " subId : " + subId + " old tech : "
                + mImsRegistrationRadioTechMap.get(phoneId, REGISTRATION_TECH_NONE)
                + " new tech : " + tech);

        if (mImsRegistrationRadioTechMap.get(phoneId, REGISTRATION_TECH_NONE) == tech) {
            // Registration tech not changed
            return false;
        }

        mImsRegistrationRadioTechMap.put(phoneId, tech);

        if (subId == INVALID_SUBSCRIPTION_ID) {
            // Need to update the cached IMS registration tech but no need to do any of the
            // following. When the SIM removed, REGISTRATION_STATE_NOT_REGISTERED is notified.
            return false;
        }

        return true;
    }

    private synchronized void onMultiSimConfigChanged(int activeModemCount) {
        // No change.
        if (mActiveModemCount == activeModemCount) return;
@@ -932,6 +998,14 @@ public class PhoneSwitcher extends Handler {
            if (phone.getImsPhone() != null) {
                phone.getImsPhone().registerForPreciseCallStateChanged(
                        this, EVENT_PRECISE_CALL_STATE_CHANGED, null);
                if (mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                    // Initialize IMS registration tech for new phoneId
                    mImsRegistrationRadioTechMap.put(phoneId, REGISTRATION_TECH_NONE);
                    ((ImsPhone) phone.getImsPhone()).registerForImsRegistrationChanges(
                            this, EVENT_IMS_RADIO_TECH_CHANGED, null);

                    log("register handler to receive IMS registration : " + phoneId);
                }
            }

            mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(),
@@ -954,8 +1028,11 @@ public class PhoneSwitcher extends Handler {

            Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
            mCurrentDdsSwitchFailure.add(ddsFailure);

            if (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                registerForImsRadioTechChange(mContext, phoneId);
            }
        }

        mAutoDataSwitchController.onMultiSimConfigChanged(activeModemCount);
    }
@@ -1121,10 +1198,14 @@ public class PhoneSwitcher extends Handler {
                    mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID;
                }
                mPhoneSubscriptions[i] = sub;

                if (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                    // Listen to IMS radio tech change for new sub
                    if (SubscriptionManager.isValidSubscriptionId(sub)) {
                        registerForImsRadioTechChange(mContext, i);
                    }
                }

                diffDetected = true;
                mAutoDataSwitchController.notifySubscriptionsMappingChanged();
            }
+36 −3
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_ME
import static android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_TITLE;
import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED;
import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERING;
import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_NONE;
import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS;
import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
@@ -264,6 +265,14 @@ public class ImsPhone extends ImsPhoneBase {
        }
    }

    /**
     * Container to transfer IMS registration radio tech.
     * This will be used as result value of AsyncResult to the handler that called
     * {@link #registerForImsRegistrationChanges(Handler, int, Object)}
     */
    public record ImsRegistrationRadioTechInfo(int phoneId, int imsRegistrationTech,
                                                int imsRegistrationState) {}

    // Instance Variables
    Phone mDefaultPhone;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -2509,7 +2518,15 @@ public class ImsPhone extends ImsPhoneBase {
            updateImsRegistrationInfo(REGISTRATION_STATE_REGISTERED,
                    attributes.getRegistrationTechnology(), SUGGESTED_ACTION_NONE,
                    imsTransportType);
            AsyncResult ar = new AsyncResult(null, null, null);

            AsyncResult ar;
            if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
                        attributes.getRegistrationTechnology(), REGISTRATION_STATE_REGISTERED),
                        null);
            } else {
                ar = new AsyncResult(null, null, null);
            }
            mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
        }

@@ -2526,7 +2543,15 @@ public class ImsPhone extends ImsPhoneBase {
            mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.PROGRESSING,
                    null);
            mImsStats.onImsRegistering(imsRadioTech);
            AsyncResult ar = new AsyncResult(null, null, null);

            AsyncResult ar;
            if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
                        imsRadioTech, REGISTRATION_STATE_REGISTERING),
                        null);
            } else {
                ar = new AsyncResult(null, null, null);
            }
            mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
        }

@@ -2569,7 +2594,15 @@ public class ImsPhone extends ImsPhoneBase {
                setCurrentSubscriberUris(null);
                clearPhoneNumberForSourceIms();
            }
            AsyncResult ar = new AsyncResult(null, null, null);

            AsyncResult ar;
            if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
                ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
                        REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED),
                        null);
            } else {
                ar = new AsyncResult(null, null, null);
            }
            mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
        }

+79 −0
Original line number Diff line number Diff line
@@ -23,9 +23,12 @@ import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
import static android.telephony.TelephonyManager.SIM_STATE_LOADED;
import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED;
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;

import static com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_VOICE_CALL_END;
import static com.android.internal.telephony.data.PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
@@ -86,6 +89,8 @@ import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;

import org.junit.After;
@@ -1789,9 +1794,77 @@ public class PhoneSwitcherTest extends TelephonyTest {

        notifyImsRegistrationTechChange(mPhone);

        // Verify that the callback is re-registered when the IMS registration callback is called.
        verify(mMockImsRegisterCallback, times(2)).setCallback(any(), anyInt(), any(), any());
    }

    @Test
    @SmallTest
    public void testReceivingImsRegistrationTech() throws Exception {
        doReturn(true).when(mFeatureFlags).changeMethodOfObtainingImsRegistrationRadioTech();

        // Set up input and output for testing
        ImsPhone testImsPhone = mock(ImsPhone.class);
        doReturn(testImsPhone).when(mPhone).getImsPhone();
        doReturn(testImsPhone).when(mPhone2).getImsPhone();
        ImsPhoneCall testImsPhoneCall = mock(ImsPhoneCall.class);
        doReturn(Call.State.IDLE).when(testImsPhoneCall).getState();
        doReturn(true).when(testImsPhoneCall).isIdle();
        doReturn(testImsPhoneCall).when(testImsPhone).getForegroundCall();
        doReturn(testImsPhoneCall).when(testImsPhone).getBackgroundCall();
        doReturn(testImsPhoneCall).when(testImsPhone).getRingingCall();

        doNothing().when(testImsPhone).registerForImsRegistrationChanges(any(), anyInt(), any());

        initialize();
        setAllPhonesInactive();

        // Phone 0 has sub 1, phone 1 has sub 2.
        // Sub 1 is default data sub.
        // Both are active subscriptions are active sub, as they are in both active slots.
        setSlotIndexToSubId(0, 1);
        setSlotIndexToSubId(1, 2);
        setDefaultDataSubId(1);
        processAllMessages();

        // Phone 0 should be the default data phoneId.
        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());

        doReturn(true).when(mPhone).isUserDataEnabled();
        mockImsRegTech(0, REGISTRATION_TECH_NONE);
        mockImsRegisterCallback(0);
        mockImsRegisterCallback(1);

        AsyncResult ar = new AsyncResult(null, new ImsPhone.ImsRegistrationRadioTechInfo(
                0, REGISTRATION_TECH_LTE, REGISTRATION_STATE_REGISTERED), null);
        notifyImsRegistrationTechChangeWithAsyncResult(ar);

        // Verify cached IMS registration tech is LTE
        assertTrue(REGISTRATION_TECH_LTE == mPhoneSwitcherUT.mImsRegistrationRadioTechMap.get(0));

        ar = new AsyncResult(null, new ImsPhone.ImsRegistrationRadioTechInfo(
                0, REGISTRATION_TECH_IWLAN, REGISTRATION_STATE_REGISTERED), null);
        notifyImsRegistrationTechChangeWithAsyncResult(ar);

        // Verify cached IMS registration tech is WiFi
        assertTrue(REGISTRATION_TECH_IWLAN
                == mPhoneSwitcherUT.mImsRegistrationRadioTechMap.get(0));

        ar = new AsyncResult(null, new ImsPhone.ImsRegistrationRadioTechInfo(
                0, REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED), null);
        notifyImsRegistrationTechChangeWithAsyncResult(ar);

        // Verify cached IMS registration tech is NONE
        assertTrue(REGISTRATION_TECH_NONE == mPhoneSwitcherUT.mImsRegistrationRadioTechMap.get(0));

        // Verify there is no crash
        notifyImsRegistrationTechChangeWithAsyncResult(null);

        // Verify that the callback is not re-registered
        // when the IMS registration callback is called.
        verify(mMockImsRegisterCallback, never()).setCallback(any(), anyInt(), any(), any());
    }

    /* Private utility methods start here */

    private void prepareIdealAutoSwitchCondition() {
@@ -1898,6 +1971,12 @@ public class PhoneSwitcherTest extends TelephonyTest {
        processAllMessages();
    }

    private void notifyImsRegistrationTechChangeWithAsyncResult(AsyncResult ar) {
        mPhoneSwitcherUT.sendMessage(
                mPhoneSwitcherUT.obtainMessage(EVENT_IMS_RADIO_TECH_CHANGED, ar));
        processAllMessages();
    }

    private Message getEcbmRegistration(Phone phone) {
        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
        ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);