Loading src/java/com/android/internal/telephony/dataconnection/TransportManager.java +46 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"; Loading Loading @@ -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. Loading Loading @@ -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."); Loading @@ -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); Loading Loading @@ -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)) { Loading Loading @@ -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); Loading @@ -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); } } /** Loading Loading @@ -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=" Loading tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java +81 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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()); } } Loading
src/java/com/android/internal/telephony/dataconnection/TransportManager.java +46 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"; Loading Loading @@ -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. Loading Loading @@ -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."); Loading @@ -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); Loading Loading @@ -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)) { Loading Loading @@ -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); Loading @@ -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); } } /** Loading Loading @@ -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=" Loading
tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java +81 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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()); } }