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

Commit ab35a558 authored by Jack Yu's avatar Jack Yu Committed by android-build-merger
Browse files

Merge "Fixed race condition in handling available networks list"

am: 4767dcbb

Change-Id: I8f8443e377ff9a255efeea7908b3f24fc7059e1f
parents 78cbf529 4767dcbb
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());
    }
}