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

Commit 79d67fe7 authored by Jack Yu's avatar Jack Yu
Browse files

Fixed IWLAN handover issues

1. Fixed a race condition issue that when connectivity
   service release network while handover is ongoing,
   TelephonyNetworkFactory incorrect added the network
   request back to the network request list. This caused
   double handover request afterwards.
2. Fixed that DcTracker cannot handle the 2nd handover request
   correctly. The 2nd request was failed immediately caused
   upper layer think the handover has already failed.

Fix: 186588930
Test: TelephonyNetworkFactoryTest, DcTrackerTest
Merged-In: I96d5f7f7f8f39abcb8cdae98af0c92281a057163
Change-Id: I96d5f7f7f8f39abcb8cdae98af0c92281a057163
(cherry picked from commit 3f903c03)
parent e607c72f
Loading
Loading
Loading
Loading
+17 −2
Original line number Original line Diff line number Diff line
@@ -2717,11 +2717,26 @@ public class DcTracker extends Handler {
            DctConstants.State state = apnContext.getState();
            DctConstants.State state = apnContext.getState();
            switch(state) {
            switch(state) {
                case CONNECTING:
                case CONNECTING:
                    if (onHandoverCompleteMsg != null) {
                        if (DBG) {
                            log("onEnableApn: already in CONNECTING state. Handover request "
                                    + "will be responded after connected.");
                        }
                        addHandoverCompleteMsg(onHandoverCompleteMsg, apnType);
                    } else {
                        if (DBG) log("onEnableApn: in CONNECTING state. Exit now.");
                    }
                    return;
                case CONNECTED:
                case CONNECTED:
                    if (DBG) log("onEnableApn: APN in " + state + " state. Exit now.");
                    if (onHandoverCompleteMsg != null) {
                    if (onHandoverCompleteMsg != null) {
                        sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType,
                        sendHandoverCompleteMsg(onHandoverCompleteMsg, true, mTransportType,
                                false);
                                false);
                        if (DBG) {
                            log("onEnableApn: already in CONNECTED state. Consider as handover "
                                    + "succeeded");
                        }
                    } else {
                        if (DBG) log("onEnableApn: APN in CONNECTED state. Exit now.");
                    }
                    }
                    return;
                    return;
                case IDLE:
                case IDLE:
+18 −2
Original line number Original line Diff line number Diff line
@@ -373,7 +373,8 @@ public class TelephonyNetworkFactory extends NetworkFactory {
                                targetTransport, onCompleteMsg);
                                targetTransport, onCompleteMsg);
                        log("Requested handover " + ApnSetting.getApnTypeString(apnType)
                        log("Requested handover " + ApnSetting.getApnTypeString(apnType)
                                + " to "
                                + " to "
                                + AccessNetworkConstants.transportTypeToString(targetTransport));
                                + AccessNetworkConstants.transportTypeToString(targetTransport)
                                + ". " + networkRequest);
                        handoverPending = true;
                        handoverPending = true;
                    } else {
                    } else {
                        // Request is there, but no actual data connection. In this case, just move
                        // Request is there, but no actual data connection. In this case, just move
@@ -429,7 +430,22 @@ public class TelephonyNetworkFactory extends NetworkFactory {
                    // connection can be re-established on the other transport.
                    // connection can be re-established on the other transport.
                    : DcTracker.RELEASE_TYPE_DETACH;
                    : DcTracker.RELEASE_TYPE_DETACH;
            releaseNetworkInternal(networkRequest, releaseType, originTransport);
            releaseNetworkInternal(networkRequest, releaseType, originTransport);

            // Before updating the network request with the target transport, make sure the request
            // is still there because it's possible that connectivity service has already released
            // the network while handover is ongoing. If connectivity service already released
            // the network request, we need to tear down the just-handovered data connection on the
            // target transport.
            if (mNetworkRequests.containsKey(networkRequest)) {
                // Update it with the target transport.
                mNetworkRequests.put(networkRequest, targetTransport);
                mNetworkRequests.put(networkRequest, targetTransport);
            } else {
                log("Network request was released before handover is completed. Now"
                        + " we need to release this network request. "
                        + networkRequest);
                releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
                        targetTransport);
            }
        } else {
        } else {
            // If handover fails and requires to fallback, the context of target transport needs to
            // If handover fails and requires to fallback, the context of target transport needs to
            // be released
            // be released
+5 −3
Original line number Original line Diff line number Diff line
@@ -320,10 +320,12 @@ public class TransportManager extends Handler {
     * @param transport The transport. Must be WWAN or WLAN.
     * @param transport The transport. Must be WWAN or WLAN.
     */
     */
    private synchronized void setCurrentTransport(@ApnType int apnType, int transport) {
    private synchronized void setCurrentTransport(@ApnType int apnType, int transport) {
        mCurrentTransports.put(apnType, transport);
        Integer previousTransport = mCurrentTransports.put(apnType, transport);
        if (previousTransport == null || previousTransport != transport) {
            logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType)
            logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType)
                    + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
                    + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
        }
        }
    }


    private boolean isHandoverPending() {
    private boolean isHandoverPending() {
        return mPendingHandoverApns.size() > 0;
        return mPendingHandoverApns.size() > 0;
+31 −0
Original line number Original line Diff line number Diff line
@@ -2429,6 +2429,12 @@ public class DcTrackerTest extends TelephonyTest {
        return (boolean) field.get(mDct);
        return (boolean) field.get(mDct);
    }
    }


    private Map<Integer, List<Message>> getHandoverCompletionMessages() throws Exception {
        Field field = DcTracker.class.getDeclaredField(("mHandoverCompletionMsgs"));
        field.setAccessible(true);
        return (Map<Integer, List<Message>>) field.get(mDct);
    }

    private void setUpTempNotMetered() {
    private void setUpTempNotMetered() {
        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR)
        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR)
                .when(mPhone).getRadioAccessFamily();
                .when(mPhone).getRadioAccessFamily();
@@ -2880,6 +2886,31 @@ public class DcTrackerTest extends TelephonyTest {
                eq(DcTracker.REQUEST_TYPE_NORMAL));
                eq(DcTracker.REQUEST_TYPE_NORMAL));
    }
    }


    @Test
    public void testHandlingSecondHandoverRequest() throws Exception {
        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
        setUpDataConnection();
        SparseArray<ApnContext> apnContextsByType = Mockito.mock(SparseArray.class);
        ConcurrentHashMap<String, ApnContext> apnContexts = Mockito.mock(ConcurrentHashMap.class);
        doReturn(mApnContext).when(apnContextsByType).get(eq(ApnSetting.TYPE_IMS));
        doReturn(mApnContext).when(apnContexts).get(eq(ApnSetting.TYPE_IMS_STRING));
        doReturn(false).when(mApnContext).isConnectable();
        doReturn(DctConstants.State.CONNECTING).when(mApnContext).getState();
        replaceInstance(DcTracker.class, "mApnContextsByType", mDct, apnContextsByType);
        replaceInstance(DcTracker.class, "mApnContexts", mDct, apnContexts);

        sendInitializationEvents();

        logd("Sending EVENT_ENABLE_APN");
        // APN id 0 is APN_TYPE_DEFAULT
        Message msg = mDct.obtainMessage(12345);
        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER, msg);
        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
        Map<Integer, List<Message>> msgs = getHandoverCompletionMessages();
        // Make sure the messages was queued properly instead of fired right away.
        assertTrue(msgs.get(ApnSetting.TYPE_IMS).contains(msg));
    }

    @Test
    @Test
    public void testNotifyDataDisconnected() {
    public void testNotifyDataDisconnected() {
        // Verify notify data disconnected on DCT constructor, initialized in setUp()
        // Verify notify data disconnected on DCT constructor, initialized in setUp()
+54 −0
Original line number Original line Diff line number Diff line
@@ -36,8 +36,10 @@ import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.os.AsyncResult;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.os.Message;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.SmallTest;
@@ -64,6 +66,8 @@ import org.mockito.Mockito;


import java.lang.reflect.Field;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;


@RunWith(AndroidTestingRunner.class)
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@TestableLooper.RunWithLooper
@@ -84,6 +88,8 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest {
    private final ArraySet<NetworkRequest> mAllNetworkRequestSet = new ArraySet<>();
    private final ArraySet<NetworkRequest> mAllNetworkRequestSet = new ArraySet<>();
    // List of requests active in DcTracker
    // List of requests active in DcTracker
    private final ArrayList<NetworkRequest> mNetworkRequestList = new ArrayList<>();
    private final ArrayList<NetworkRequest> mNetworkRequestList = new ArrayList<>();
    // List of complete messages associated with the network requests
    private final Map<NetworkRequest, Message> mNetworkRequestMessageMap = new HashMap<>();


    private TelephonyNetworkFactory mTelephonyNetworkFactoryUT;
    private TelephonyNetworkFactory mTelephonyNetworkFactoryUT;
    private int mRequestId = 0;
    private int mRequestId = 0;
@@ -162,8 +168,10 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest {


        doAnswer(invocation -> {
        doAnswer(invocation -> {
            final NetworkRequest req = (NetworkRequest) invocation.getArguments()[0];
            final NetworkRequest req = (NetworkRequest) invocation.getArguments()[0];
            final Message msg = (Message) invocation.getArguments()[2];
            mNetworkRequestList.add(req);
            mNetworkRequestList.add(req);
            mAllNetworkRequestSet.add(req);
            mAllNetworkRequestSet.add(req);
            mNetworkRequestMessageMap.put(req, msg);
            return null;
            return null;
        }).when(mDcTracker).requestNetwork(any(), anyInt(), any());
        }).when(mDcTracker).requestNetwork(any(), anyInt(), any());


@@ -424,4 +432,50 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest {
        verify(mDcTracker, times(1)).requestNetwork(any(), eq(1), any());
        verify(mDcTracker, times(1)).requestNetwork(any(), eq(1), any());
    }
    }


    @Test
    @SmallTest
    public void testNetworkRequestReleasedDuringHandover() throws Exception {
        createMockedTelephonyComponents();
        doReturn(0).when(mSubscriptionController).getSubIdUsingPhoneId(0);
        mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
                TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);

        activatePhoneInPhoneSwitcher(0, true);
        makeDefaultInternetRequest();

        NetworkRequest mmsNetworkRequest = makeSubSpecificMmsRequest(0);
        processAllMessages();

        Field f = TelephonyNetworkFactory.class.getDeclaredField("mInternalHandler");
        f.setAccessible(true);
        Handler h = (Handler) f.get(mTelephonyNetworkFactoryUT);

        HandoverCallback handoverCallback = mock(HandoverCallback.class);
        //Mockito.reset(mDcTracker);
        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByApnType(anyString());
        doReturn(true).when(mDataConnection).isActive();

        HandoverParams hp = new HandoverParams(ApnSetting.TYPE_MMS,
                AccessNetworkConstants.TRANSPORT_TYPE_WLAN, handoverCallback);
        AsyncResult ar = new AsyncResult(null, hp, null);
        h.sendMessage(h.obtainMessage(5 /* EVENT_DATA_HANDOVER_NEEDED */, ar));
        processAllMessages();

        // Now release the network request while handover is still ongoing.
        mTelephonyNetworkFactoryUT.releaseNetworkFor(mmsNetworkRequest);
        processAllMessages();

        Message msg = mNetworkRequestMessageMap.get(mmsNetworkRequest);

        Bundle bundle = msg.getData();
        bundle.putParcelable("extra_network_request", mmsNetworkRequest);
        bundle.putBoolean("extra_success", true);
        bundle.putInt("extra_transport_type", AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
        bundle.putBoolean("extra_handover_failure_fallback", false);
        h.sendMessage(msg);
        processAllMessages();

        // Ensure the release is called one more time after the normal release
        verify(mDcTracker, times(2)).releaseNetwork(any(), eq(1));
    }
}
}