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

Commit 32adae60 authored by Jack Yu's avatar Jack Yu
Browse files

Fixed race condition in handling available networks list

When modem reports back to back available networks list, we
should batch process them.

Test: Unit tests + manual
Bug: 132113909
Merged-In: I0071ae6812bbc698a41ba1a303971032a84f2034
Change-Id: I0071ae6812bbc698a41ba1a303971032a84f2034
(cherry picked from commit eecee44c)
parent f82d0598
Loading
Loading
Loading
Loading
+46 −5
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -114,6 +115,8 @@ public class TransportManager extends Handler {

    private static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 1;

    private static final int EVENT_UPDATE_AVAILABLE_NETWORKS = 2;

    public static final String SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE =
            "ro.telephony.iwlan_operation_mode";

@@ -155,11 +158,16 @@ public class TransportManager extends Handler {
    private AccessNetworksManager mAccessNetworksManager;

    /**
     * Available networks. The key is the APN type, and the value is the available network list in
     * the preferred order.
     * Current available networks. The key is the APN type, and the value is the available network
     * list in the preferred order.
     */
    private final SparseArray<int[]> mCurrentAvailableNetworks;

    /**
     * The queued available networks list.
     */
    private final LinkedList<List<QualifiedNetworks>> mAvailableNetworksList;

    /**
     * The current transport of the APN type. The key is the APN type, and the value is the
     * transport.
@@ -211,6 +219,7 @@ public class TransportManager extends Handler {
        mCurrentTransports = new ConcurrentHashMap<>();
        mPendingHandoverApns = new SparseIntArray();
        mHandoverNeededEventRegistrants = new RegistrantList();
        mAvailableNetworksList = new LinkedList<>();

        if (isInLegacyMode()) {
            log("operates in legacy mode.");
@@ -233,7 +242,11 @@ public class TransportManager extends Handler {
            case EVENT_QUALIFIED_NETWORKS_CHANGED:
                AsyncResult ar = (AsyncResult) msg.obj;
                List<QualifiedNetworks> networks = (List<QualifiedNetworks>) ar.result;
                updateAvailableNetworks(networks);
                mAvailableNetworksList.add(networks);
                sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS);
                break;
            case EVENT_UPDATE_AVAILABLE_NETWORKS:
                updateAvailableNetworks();
                break;
            default:
                loge("Unexpected event " + msg.what);
@@ -297,13 +310,28 @@ public class TransportManager extends Handler {
     * @param apnType The APN type
     * @param transport The transport. Must be WWAN or WLAN.
     */
    private void setCurrentTransport(@ApnType int apnType, int transport) {
    private synchronized void setCurrentTransport(@ApnType int apnType, int transport) {
        mCurrentTransports.put(apnType, transport);
        logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType)
                + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
    }

    private synchronized void updateAvailableNetworks(List<QualifiedNetworks> networksList) {
    private boolean isHandoverPending() {
        return mPendingHandoverApns.size() > 0;
    }

    private void updateAvailableNetworks() {
        if (isHandoverPending()) {
            log("There's ongoing handover. Will update networks once handover completed.");
            return;
        }

        if (mAvailableNetworksList.size() == 0) {
            log("Nothing in the available network list queue.");
            return;
        }

        List<QualifiedNetworks> networksList = mAvailableNetworksList.remove();
        logl("updateAvailableNetworks: " + networksList);
        for (QualifiedNetworks networks : networksList) {
            if (areNetworksValid(networks)) {
@@ -334,6 +362,12 @@ public class TransportManager extends Handler {
                                // to the new one. If failed, there will be retry afterwards anyway.
                                setCurrentTransport(networks.apnType, targetTransport);
                                mPendingHandoverApns.delete(networks.apnType);

                                // If there are still pending available network changes, we need to
                                // process the rest.
                                if (mAvailableNetworksList.size() > 0) {
                                    sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS);
                                }
                            }));
                }
                mCurrentAvailableNetworks.put(networks.apnType, networks.qualifiedNetworks);
@@ -341,6 +375,11 @@ public class TransportManager extends Handler {
                loge("Invalid networks received: " + networks);
            }
        }

        // If there are still pending available network changes, we need to process the rest.
        if (mAvailableNetworksList.size() > 0) {
            sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS);
        }
    }

    /**
@@ -420,6 +459,8 @@ public class TransportManager extends Handler {
                .mapToObj(type -> AccessNetworkConstants.transportTypeToString(type))
                .collect(Collectors.joining(",")) + "]");
        pw.println("mCurrentAvailableNetworks=" + mCurrentAvailableNetworks);
        pw.println("mAvailableNetworksList=" + mAvailableNetworksList);
        pw.println("mPendingHandoverApns=" + mPendingHandoverApns);
        pw.println("mCurrentTransports=" + mCurrentTransports);
        pw.println("isInLegacy=" + isInLegacyMode());
        pw.println("IWLAN operation mode="
+81 −0
Original line number Diff line number Diff line
@@ -44,8 +44,10 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class TransportManagerTest extends TelephonyTest {
@@ -188,4 +190,83 @@ public class TransportManagerTest extends TelephonyTest {
        // Verify handover needed event was not sent
        verify(mTestHandler, never()).sendMessageAtTime(any(Message.class), anyLong());
    }

    private LinkedList<List<QualifiedNetworks>> getAvailableNetworksList() throws Exception {
        Field f = TransportManager.class.getDeclaredField("mAvailableNetworksList");
        f.setAccessible(true);
        return (LinkedList<List<QualifiedNetworks>>) f.get(mTransportManager);
    }

    @Test
    @SmallTest
    public void testBackToBackHandoverNeeded() throws Exception {
        mTransportManager.registerForHandoverNeededEvent(mTestHandler, EVENT_HANDOVER_NEEDED);

        // Initial qualified networks
        List<QualifiedNetworks> networkList = new ArrayList<>(Arrays.asList(
                new QualifiedNetworks(ApnSetting.TYPE_IMS,
                        new int[]{AccessNetworkType.EUTRAN, AccessNetworkType.UTRAN,
                                AccessNetworkType.IWLAN})));
        mTransportManager.obtainMessage(1 /* EVENT_QUALIFIED_NETWORKS_CHANGED */,
                new AsyncResult(null, networkList, null)).sendToTarget();
        waitForMs(100);
        // Verify handover needed event was not sent
        verify(mTestHandler, never()).sendMessageAtTime(any(Message.class), anyLong());

        assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                mTransportManager.getCurrentTransport(ApnSetting.TYPE_IMS));

        // Now change the order of qualified networks by putting IWLAN first
        networkList = new ArrayList<>(Arrays.asList(
                new QualifiedNetworks(ApnSetting.TYPE_IMS,
                        new int[]{AccessNetworkType.IWLAN, AccessNetworkType.UTRAN,
                                AccessNetworkType.EUTRAN})));
        mTransportManager.obtainMessage(1 /* EVENT_QUALIFIED_NETWORKS_CHANGED */,
                new AsyncResult(null, networkList, null)).sendToTarget();
        waitForMs(100);

        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);

        // Verify handover needed event was sent and the the target transport is WLAN.
        verify(mTestHandler, times(1)).sendMessageAtTime(messageArgumentCaptor.capture(),
                anyLong());
        Message message = messageArgumentCaptor.getValue();
        assertEquals(EVENT_HANDOVER_NEEDED, message.what);
        AsyncResult ar = (AsyncResult) message.obj;
        HandoverParams params = (HandoverParams) ar.result;
        assertEquals(ApnSetting.TYPE_IMS, params.apnType);
        assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, params.targetTransport);

        // Before handover is completed, update the available networks again.
        // This time change the order of qualified networks by putting EUTRAN first
        networkList = new ArrayList<>(Arrays.asList(
                new QualifiedNetworks(ApnSetting.TYPE_IMS,
                        new int[]{AccessNetworkType.EUTRAN, AccessNetworkType.UTRAN,
                                AccessNetworkType.IWLAN})));
        mTransportManager.obtainMessage(1 /* EVENT_QUALIFIED_NETWORKS_CHANGED */,
                new AsyncResult(null, networkList, null)).sendToTarget();
        waitForMs(100);

        // Verify handover needed event was sent only once (for the previous change)
        verify(mTestHandler, times(1)).sendMessageAtTime(messageArgumentCaptor.capture(),
                anyLong());

        LinkedList<List<QualifiedNetworks>> listQueue = getAvailableNetworksList();
        // Verify the list has been queued.
        assertEquals(1, listQueue.size());

        // Notify handover succeeded.
        params.callback.onCompleted(true);
        assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
                mTransportManager.getCurrentTransport(ApnSetting.TYPE_IMS));
        waitForMs(100);

        listQueue = getAvailableNetworksList();
        // Verify the queue is empty.
        assertEquals(0, listQueue.size());

        // Verify handover 2nd needed event was sent
        verify(mTestHandler, times(2)).sendMessageAtTime(messageArgumentCaptor.capture(),
                anyLong());
    }
}