Loading src/java/com/android/internal/telephony/dataconnection/DcTracker.java +17 −2 Original line number Diff line number Diff line Loading @@ -2716,11 +2716,26 @@ public class DcTracker extends Handler { DctConstants.State state = apnContext.getState(); switch(state) { 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: if (DBG) log("onEnableApn: APN in " + state + " state. Exit now."); if (onHandoverCompleteMsg != null) { sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, sendHandoverCompleteMsg(onHandoverCompleteMsg, true, mTransportType, 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; case IDLE: Loading src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java +18 −2 Original line number Diff line number Diff line Loading @@ -373,7 +373,8 @@ public class TelephonyNetworkFactory extends NetworkFactory { targetTransport, onCompleteMsg); log("Requested handover " + ApnSetting.getApnTypeString(apnType) + " to " + AccessNetworkConstants.transportTypeToString(targetTransport)); + AccessNetworkConstants.transportTypeToString(targetTransport) + ". " + networkRequest); handoverPending = true; } else { // Request is there, but no actual data connection. In this case, just move Loading Loading @@ -429,7 +430,22 @@ public class TelephonyNetworkFactory extends NetworkFactory { // connection can be re-established on the other transport. : DcTracker.RELEASE_TYPE_DETACH; 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); } 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 { // If handover fails and requires to fallback, the context of target transport needs to // be released Loading src/java/com/android/internal/telephony/dataconnection/TransportManager.java +5 −3 Original line number Diff line number Diff line Loading @@ -320,10 +320,12 @@ public class TransportManager extends Handler { * @param transport The transport. Must be WWAN or WLAN. */ 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) + ", transport=" + AccessNetworkConstants.transportTypeToString(transport)); } } private boolean isHandoverPending() { return mPendingHandoverApns.size() > 0; Loading tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java +31 −0 Original line number Diff line number Diff line Loading @@ -2361,6 +2361,12 @@ public class DcTrackerTest extends TelephonyTest { 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() { doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR) .when(mPhone).getRadioAccessFamily(); Loading Loading @@ -2809,4 +2815,29 @@ public class DcTrackerTest extends TelephonyTest { eq(RetryManager.NO_SUGGESTED_RETRY_DELAY), 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)); } } tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -36,8 +36,10 @@ import android.net.NetworkProvider; import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; import android.os.AsyncResult; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.telephony.AccessNetworkConstants; import android.telephony.data.ApnSetting; import android.test.suitebuilder.annotation.SmallTest; Loading @@ -64,6 +66,8 @@ import org.mockito.Mockito; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper Loading @@ -84,6 +88,8 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest { private final ArraySet<NetworkRequest> mAllNetworkRequestSet = new ArraySet<>(); // List of requests active in DcTracker 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 int mRequestId = 0; Loading Loading @@ -162,8 +168,10 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest { doAnswer(invocation -> { final NetworkRequest req = (NetworkRequest) invocation.getArguments()[0]; final Message msg = (Message) invocation.getArguments()[2]; mNetworkRequestList.add(req); mAllNetworkRequestSet.add(req); mNetworkRequestMessageMap.put(req, msg); return null; }).when(mDcTracker).requestNetwork(any(), anyInt(), any()); Loading Loading @@ -424,4 +432,50 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest { 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)); } } Loading
src/java/com/android/internal/telephony/dataconnection/DcTracker.java +17 −2 Original line number Diff line number Diff line Loading @@ -2716,11 +2716,26 @@ public class DcTracker extends Handler { DctConstants.State state = apnContext.getState(); switch(state) { 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: if (DBG) log("onEnableApn: APN in " + state + " state. Exit now."); if (onHandoverCompleteMsg != null) { sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, sendHandoverCompleteMsg(onHandoverCompleteMsg, true, mTransportType, 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; case IDLE: Loading
src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java +18 −2 Original line number Diff line number Diff line Loading @@ -373,7 +373,8 @@ public class TelephonyNetworkFactory extends NetworkFactory { targetTransport, onCompleteMsg); log("Requested handover " + ApnSetting.getApnTypeString(apnType) + " to " + AccessNetworkConstants.transportTypeToString(targetTransport)); + AccessNetworkConstants.transportTypeToString(targetTransport) + ". " + networkRequest); handoverPending = true; } else { // Request is there, but no actual data connection. In this case, just move Loading Loading @@ -429,7 +430,22 @@ public class TelephonyNetworkFactory extends NetworkFactory { // connection can be re-established on the other transport. : DcTracker.RELEASE_TYPE_DETACH; 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); } 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 { // If handover fails and requires to fallback, the context of target transport needs to // be released Loading
src/java/com/android/internal/telephony/dataconnection/TransportManager.java +5 −3 Original line number Diff line number Diff line Loading @@ -320,10 +320,12 @@ public class TransportManager extends Handler { * @param transport The transport. Must be WWAN or WLAN. */ 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) + ", transport=" + AccessNetworkConstants.transportTypeToString(transport)); } } private boolean isHandoverPending() { return mPendingHandoverApns.size() > 0; Loading
tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java +31 −0 Original line number Diff line number Diff line Loading @@ -2361,6 +2361,12 @@ public class DcTrackerTest extends TelephonyTest { 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() { doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR) .when(mPhone).getRadioAccessFamily(); Loading Loading @@ -2809,4 +2815,29 @@ public class DcTrackerTest extends TelephonyTest { eq(RetryManager.NO_SUGGESTED_RETRY_DELAY), 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)); } }
tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -36,8 +36,10 @@ import android.net.NetworkProvider; import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; import android.os.AsyncResult; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.telephony.AccessNetworkConstants; import android.telephony.data.ApnSetting; import android.test.suitebuilder.annotation.SmallTest; Loading @@ -64,6 +66,8 @@ import org.mockito.Mockito; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper Loading @@ -84,6 +88,8 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest { private final ArraySet<NetworkRequest> mAllNetworkRequestSet = new ArraySet<>(); // List of requests active in DcTracker 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 int mRequestId = 0; Loading Loading @@ -162,8 +168,10 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest { doAnswer(invocation -> { final NetworkRequest req = (NetworkRequest) invocation.getArguments()[0]; final Message msg = (Message) invocation.getArguments()[2]; mNetworkRequestList.add(req); mAllNetworkRequestSet.add(req); mNetworkRequestMessageMap.put(req, msg); return null; }).when(mDcTracker).requestNetwork(any(), anyInt(), any()); Loading Loading @@ -424,4 +432,50 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest { 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)); } }