Loading src/java/com/android/internal/telephony/dataconnection/DcTracker.java +35 −9 Original line number Diff line number Diff line Loading @@ -1560,6 +1560,11 @@ public class DcTracker extends Handler { return isInEcm && !isInImsEcm; } private boolean isHandoverPending(@ApnType int apnType) { List<Message> messageList = mHandoverCompletionMsgs.get(apnType); return messageList != null && messageList.size() > 0; } private void trySetupData(ApnContext apnContext, @RequestNetworkType int requestType, @Nullable Message onHandoverCompleteMsg) { if (onHandoverCompleteMsg != null) { Loading Loading @@ -1600,6 +1605,19 @@ public class DcTracker extends Handler { str.append("isDataEnabled() = false. " + mDataEnabledSettings); } // Check if it fails because of the existing data is still disconnecting. if (dataConnectionReasons.containsOnly( DataDisallowedReasonType.DATA_IS_DISCONNECTING) && isHandoverPending(apnContext.getApnTypeBitmask())) { // Normally we don't retry when isDataAllow() returns false, because that's consider // pre-condition not met, for example, data not enabled by the user, or airplane // mode is on. If we retry in those cases, there will be significant power impact. // DATA_IS_DISCONNECTING is a special case we want to retry, and for the handover // case only. log("Data is disconnecting. Will retry handover later."); return; } // If this is a data retry, we should set the APN state to FAILED so it won't stay // in RETRYING forever. if (apnContext.getState() == DctConstants.State.RETRYING) { Loading Loading @@ -2369,9 +2387,10 @@ public class DcTracker extends Handler { sendMessageDelayed(msg, delay); if (DBG) { log("startReconnect: delay=" + delay + " apn=" + apnContext + "reason: " + apnContext.getReason() + " subId=" + mPhone.getSubId() + " request type=" + requestType); log("startReconnect: delay=" + delay + ", apn=" + apnContext + ", reason=" + apnContext.getReason() + ", subId=" + mPhone.getSubId() + ", request type=" + requestTypeToString(requestType)); } } Loading Loading @@ -2683,7 +2702,6 @@ public class DcTracker extends Handler { switch(state) { case CONNECTING: case CONNECTED: case DISCONNECTING: if (DBG) log("onEnableApn: APN in " + state + " state. Exit now."); if (onHandoverCompleteMsg != null) { sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, Loading Loading @@ -3288,12 +3306,20 @@ public class DcTracker extends Handler { // we're not tying up the RIL command channel. // This also helps in any external dependency to turn off the context. if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect"); // See if there are still handover request pending that we need to retry handover // after previous data gets disconnected. if (isHandoverPending(apnContext.getApnTypeBitmask())) { if (DBG) log("Handover request pending. Retry handover immediately."); startReconnect(0, apnContext, REQUEST_TYPE_HANDOVER); } else { long delay = apnContext.getRetryAfterDisconnectDelay(); if (delay > 0) { // Data connection is in IDLE state, so when we reconnect later, we'll rebuild // the waiting APN list, which will also reset/reconfigure the retry manager. startReconnect(delay, apnContext, REQUEST_TYPE_NORMAL); } } } else { boolean restartRadioAfterProvisioning = mPhone.getContext().getResources().getBoolean( com.android.internal.R.bool.config_restartRadioAfterProvisioning); Loading tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java +55 −0 Original line number Diff line number Diff line Loading @@ -86,6 +86,7 @@ import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; import android.util.Pair; import android.util.SparseArray; import androidx.test.filters.FlakyTest; Loading @@ -101,6 +102,7 @@ import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; Loading @@ -115,6 +117,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; Loading Loading @@ -807,6 +810,18 @@ public class DcTrackerTest extends TelephonyTest { } } private boolean isHandoverPending(int apnType) { try { Method method = DcTracker.class.getDeclaredMethod("isHandoverPending", int.class); method.setAccessible(true); return (boolean) method.invoke(mDct, apnType); } catch (Exception e) { fail(e.toString()); return false; } } private void sendInitializationEvents() { sendCarrierConfigChanged(""); Loading Loading @@ -2737,4 +2752,44 @@ public class DcTrackerTest extends TelephonyTest { } waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler()); } @Test public void testRetryHandoverWhenDisconnecting() 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.DISCONNECTING).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 mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER, mDct.obtainMessage(12345)); waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler()); assertTrue(isHandoverPending(ApnSetting.TYPE_IMS)); // Verify no handover request was sent verify(mDataConnection, never()).bringUp(any(ApnContext.class), anyInt(), anyInt(), any(Message.class), anyInt(), anyInt(), anyInt(), anyBoolean()); doReturn(DctConstants.State.RETRYING).when(mApnContext).getState(); // Data now is disconnected doReturn(true).when(mApnContext).isConnectable(); mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, new AsyncResult(Pair.create(mApnContext, 0), null, null))); waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler()); verify(mDataConnection).bringUp(any(ApnContext.class), anyInt(), anyInt(), any(Message.class), anyInt(), eq(DcTracker.REQUEST_TYPE_HANDOVER), anyInt(), anyBoolean()); } } Loading
src/java/com/android/internal/telephony/dataconnection/DcTracker.java +35 −9 Original line number Diff line number Diff line Loading @@ -1560,6 +1560,11 @@ public class DcTracker extends Handler { return isInEcm && !isInImsEcm; } private boolean isHandoverPending(@ApnType int apnType) { List<Message> messageList = mHandoverCompletionMsgs.get(apnType); return messageList != null && messageList.size() > 0; } private void trySetupData(ApnContext apnContext, @RequestNetworkType int requestType, @Nullable Message onHandoverCompleteMsg) { if (onHandoverCompleteMsg != null) { Loading Loading @@ -1600,6 +1605,19 @@ public class DcTracker extends Handler { str.append("isDataEnabled() = false. " + mDataEnabledSettings); } // Check if it fails because of the existing data is still disconnecting. if (dataConnectionReasons.containsOnly( DataDisallowedReasonType.DATA_IS_DISCONNECTING) && isHandoverPending(apnContext.getApnTypeBitmask())) { // Normally we don't retry when isDataAllow() returns false, because that's consider // pre-condition not met, for example, data not enabled by the user, or airplane // mode is on. If we retry in those cases, there will be significant power impact. // DATA_IS_DISCONNECTING is a special case we want to retry, and for the handover // case only. log("Data is disconnecting. Will retry handover later."); return; } // If this is a data retry, we should set the APN state to FAILED so it won't stay // in RETRYING forever. if (apnContext.getState() == DctConstants.State.RETRYING) { Loading Loading @@ -2369,9 +2387,10 @@ public class DcTracker extends Handler { sendMessageDelayed(msg, delay); if (DBG) { log("startReconnect: delay=" + delay + " apn=" + apnContext + "reason: " + apnContext.getReason() + " subId=" + mPhone.getSubId() + " request type=" + requestType); log("startReconnect: delay=" + delay + ", apn=" + apnContext + ", reason=" + apnContext.getReason() + ", subId=" + mPhone.getSubId() + ", request type=" + requestTypeToString(requestType)); } } Loading Loading @@ -2683,7 +2702,6 @@ public class DcTracker extends Handler { switch(state) { case CONNECTING: case CONNECTED: case DISCONNECTING: if (DBG) log("onEnableApn: APN in " + state + " state. Exit now."); if (onHandoverCompleteMsg != null) { sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, Loading Loading @@ -3288,12 +3306,20 @@ public class DcTracker extends Handler { // we're not tying up the RIL command channel. // This also helps in any external dependency to turn off the context. if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect"); // See if there are still handover request pending that we need to retry handover // after previous data gets disconnected. if (isHandoverPending(apnContext.getApnTypeBitmask())) { if (DBG) log("Handover request pending. Retry handover immediately."); startReconnect(0, apnContext, REQUEST_TYPE_HANDOVER); } else { long delay = apnContext.getRetryAfterDisconnectDelay(); if (delay > 0) { // Data connection is in IDLE state, so when we reconnect later, we'll rebuild // the waiting APN list, which will also reset/reconfigure the retry manager. startReconnect(delay, apnContext, REQUEST_TYPE_NORMAL); } } } else { boolean restartRadioAfterProvisioning = mPhone.getContext().getResources().getBoolean( com.android.internal.R.bool.config_restartRadioAfterProvisioning); Loading
tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java +55 −0 Original line number Diff line number Diff line Loading @@ -86,6 +86,7 @@ import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; import android.util.Pair; import android.util.SparseArray; import androidx.test.filters.FlakyTest; Loading @@ -101,6 +102,7 @@ import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; Loading @@ -115,6 +117,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; Loading Loading @@ -807,6 +810,18 @@ public class DcTrackerTest extends TelephonyTest { } } private boolean isHandoverPending(int apnType) { try { Method method = DcTracker.class.getDeclaredMethod("isHandoverPending", int.class); method.setAccessible(true); return (boolean) method.invoke(mDct, apnType); } catch (Exception e) { fail(e.toString()); return false; } } private void sendInitializationEvents() { sendCarrierConfigChanged(""); Loading Loading @@ -2737,4 +2752,44 @@ public class DcTrackerTest extends TelephonyTest { } waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler()); } @Test public void testRetryHandoverWhenDisconnecting() 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.DISCONNECTING).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 mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER, mDct.obtainMessage(12345)); waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler()); assertTrue(isHandoverPending(ApnSetting.TYPE_IMS)); // Verify no handover request was sent verify(mDataConnection, never()).bringUp(any(ApnContext.class), anyInt(), anyInt(), any(Message.class), anyInt(), anyInt(), anyInt(), anyBoolean()); doReturn(DctConstants.State.RETRYING).when(mApnContext).getState(); // Data now is disconnected doReturn(true).when(mApnContext).isConnectable(); mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, new AsyncResult(Pair.create(mApnContext, 0), null, null))); waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler()); verify(mDataConnection).bringUp(any(ApnContext.class), anyInt(), anyInt(), any(Message.class), anyInt(), eq(DcTracker.REQUEST_TYPE_HANDOVER), anyInt(), anyBoolean()); } }