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

Commit bde4d599 authored by Jack Yu's avatar Jack Yu Committed by Gerrit Code Review
Browse files

Merge changes I16a9f413,I153bd511,I76f52a44,I5d9b3860

* changes:
  Added handover retry when previous data is still disconnecting
  Renamed network request complete message
  Send request network complete messages as needed if no more retry
  Clean up handover complete message in handover retry case
parents 4298f0a0 a2ecb508
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -403,14 +403,22 @@ public class ApnContext {
        }
    }

    /**
     * Request a network
     *
     * @param networkRequest Network request from clients
     * @param type The request type
     * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
     * handover is completed. For normal request, this should be null.
     */
    public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
                               Message onCompleteMsg) {
            Message onHandoverCompleteMsg) {
        synchronized (mRefCountLock) {
            mNetworkRequests.add(networkRequest);
            logl("requestNetwork for " + networkRequest + ", type="
                    + DcTracker.requestTypeToString(type));
            mDcTracker.enableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type,
                    onCompleteMsg);
                    onHandoverCompleteMsg);
            if (mDataConnection != null) {
                // New network request added. Should re-evaluate properties of
                // the data connection. For example, the score may change.
+139 −103
Original line number Diff line number Diff line
@@ -209,10 +209,9 @@ public class DcTracker extends Handler {
     */
    public static final int RELEASE_TYPE_HANDOVER = 3;

    /** The extras for request network completion message */
    /** The extras for handover completion message */
    static final String DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST = "extra_network_request";
    static final String DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE = "extra_transport_type";
    static final String DATA_COMPLETE_MSG_EXTRA_REQUEST_TYPE = "extra_request_type";
    static final String DATA_COMPLETE_MSG_EXTRA_SUCCESS = "extra_success";
    /**
     * The flag indicates whether after handover failure, the data connection should remain on the
@@ -721,7 +720,7 @@ public class DcTracker extends Handler {
     * messages to be sent. Using a list because there might be multiple network requests for
     * the same APN type.
     */
    private final Map<Integer, List<Message>> mRequestNetworkCompletionMsgs = new HashMap<>();
    private final Map<Integer, List<Message>> mHandoverCompletionMsgs = new HashMap<>();

    //***** Constructor
    public DcTracker(Phone phone, @TransportType int transportType) {
@@ -948,12 +947,24 @@ public class DcTracker extends Handler {
        mPhone.notifyDataActivity();
    }

    /**
     * Request a network
     *
     * @param networkRequest Network request from clients
     * @param type The request type
     * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
     * handover is completed. For normal request, this should be null.
     */
    public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
                               Message onCompleteMsg) {
            Message onHandoverCompleteMsg) {
        if (type != REQUEST_TYPE_HANDOVER && onHandoverCompleteMsg != null) {
            throw new RuntimeException("request network with normal type request type but passing "
                    + "handover complete message.");
        }
        final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
        final ApnContext apnContext = mApnContextsByType.get(apnType);
        if (apnContext != null) {
            apnContext.requestNetwork(networkRequest, type, onCompleteMsg);
            apnContext.requestNetwork(networkRequest, type, onHandoverCompleteMsg);
        }
    }

@@ -1523,7 +1534,7 @@ public class DcTracker extends Handler {
        if (apnContext.isConnectable()) {
            log("isConnectable() call trySetupData");
            apnContext.setReason(reason);
            trySetupData(apnContext, REQUEST_TYPE_NORMAL);
            trySetupData(apnContext, REQUEST_TYPE_NORMAL, null);
        }
    }

@@ -1534,12 +1545,21 @@ public class DcTracker extends Handler {
        return isInEcm && !isInImsEcm;
    }

    private boolean trySetupData(ApnContext apnContext, @RequestNetworkType int requestType) {
    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) {
            addHandoverCompleteMsg(onHandoverCompleteMsg, apnContext.getApnTypeBitmask());
        }

        if (mPhone.getSimulatedRadioControl() != null) {
            // Assume data is connected on the simulator
            log("trySetupData: X We're on the simulator; assuming connected retValue=true");
            return true;
            return;
        }

        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
@@ -1549,7 +1569,44 @@ public class DcTracker extends Handler {
                + ". " + dataConnectionReasons.toString();
        if (DBG) log(logStr);
        apnContext.requestLog(logStr);
        if (isDataAllowed) {
        if (!isDataAllowed) {
            StringBuilder str = new StringBuilder();

            str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType()
                    + ", mState=" + apnContext.getState() + ", apnEnabled="
                    + apnContext.isEnabled() + ", mDependencyMet="
                    + apnContext.isDependencyMet() + "] ");

            if (!mDataEnabledSettings.isDataEnabled()) {
                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) {
                apnContext.setState(DctConstants.State.FAILED);
                str.append(" Stop retrying.");
            }

            if (DBG) log(str.toString());
            apnContext.requestLog(str.toString());
            sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false);
            return;
        }

        if (apnContext.getState() == DctConstants.State.FAILED) {
            String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
            if (DBG) log(str);
@@ -1571,7 +1628,9 @@ public class DcTracker extends Handler {
                String str = "trySetupData: X No APN found retValue=false";
                if (DBG) log(str);
                apnContext.requestLog(str);
                    return false;
                sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false,
                        false);
                return;
            } else {
                apnContext.setWaitingApns(waitingApns);
                if (DBG) {
@@ -1581,32 +1640,8 @@ public class DcTracker extends Handler {
            }
        }

            boolean retValue = setupData(apnContext, radioTech, requestType);

            if (DBG) log("trySetupData: X retValue=" + retValue);
            return retValue;
        } else {
            StringBuilder str = new StringBuilder();

            str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType()
                    + ", mState=" + apnContext.getState() + ", apnEnabled="
                    + apnContext.isEnabled() + ", mDependencyMet="
                    + apnContext.isDependencyMet() + "] ");

            if (!mDataEnabledSettings.isDataEnabled()) {
                str.append("isDataEnabled() = false. " + mDataEnabledSettings);
            }

            // 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) {
                apnContext.setState(DctConstants.State.FAILED);
                str.append(" Stop retrying.");
            }

            if (DBG) log(str.toString());
            apnContext.requestLog(str.toString());
            return false;
        if (!setupData(apnContext, radioTech, requestType)) {
            sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false);
        }
    }

@@ -2321,9 +2356,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));
        }
    }

@@ -2522,25 +2558,33 @@ public class DcTracker extends Handler {
        return apnSetting.canHandleType(apnType);
    }

    private void addRequestNetworkCompleteMsg(Message onCompleteMsg,
    private void addHandoverCompleteMsg(Message onCompleteMsg,
            @ApnType int apnType) {
        if (onCompleteMsg != null) {
            List<Message> messageList = mRequestNetworkCompletionMsgs.get(apnType);
            List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
            if (messageList == null) messageList = new ArrayList<>();
            messageList.add(onCompleteMsg);
            mRequestNetworkCompletionMsgs.put(apnType, messageList);
            mHandoverCompletionMsgs.put(apnType, messageList);
        }
    }

    private void sendRequestNetworkCompleteMsg(Message message, boolean success,
                                               @TransportType int transport,
                                               @RequestNetworkType int requestType,
                                               boolean doFallbackOnFailedHandover) {
    private void sendHandoverCompleteMessages(@ApnType int apnType, boolean success,
            boolean fallbackOnFailedHandover) {
        List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
        if (messageList != null) {
            for (Message msg : messageList) {
                sendHandoverCompleteMsg(msg, success, mTransportType, fallbackOnFailedHandover);
            }
            messageList.clear();
        }
    }

    private void sendHandoverCompleteMsg(Message message, boolean success,
            @TransportType int transport, boolean doFallbackOnFailedHandover) {
        if (message == null) return;

        Bundle b = message.getData();
        b.putBoolean(DATA_COMPLETE_MSG_EXTRA_SUCCESS, success);
        b.putInt(DATA_COMPLETE_MSG_EXTRA_REQUEST_TYPE, requestType);
        b.putInt(DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE, transport);
        b.putBoolean(DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK, doFallbackOnFailedHandover);
        message.sendToTarget();
@@ -2590,18 +2634,19 @@ public class DcTracker extends Handler {
    }

    public void enableApn(@ApnType int apnType, @RequestNetworkType int requestType,
            Message onCompleteMsg) {
            Message onHandoverCompleteMsg) {
        sendMessage(obtainMessage(DctConstants.EVENT_ENABLE_APN, apnType, requestType,
                onCompleteMsg));
                onHandoverCompleteMsg));
    }

    private void onEnableApn(@ApnType int apnType, @RequestNetworkType int requestType,
                             Message onCompleteMsg) {
            Message onHandoverCompleteMsg) {
        ApnContext apnContext = mApnContextsByType.get(apnType);
        if (apnContext == null) {
            loge("onEnableApn(" + apnType + "): NO ApnContext");
            sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType,
                    false);
            if (onHandoverCompleteMsg != null) {
                sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false);
            }
            return;
        }

@@ -2616,8 +2661,9 @@ public class DcTracker extends Handler {
            str = "onEnableApn: dependency is not met.";
            if (DBG) log(str);
            apnContext.requestLog(str);
            sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType,
                    false);
            if (onHandoverCompleteMsg != null) {
                sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false);
            }
            return;
        }

@@ -2625,24 +2671,14 @@ public class DcTracker extends Handler {
            DctConstants.State state = apnContext.getState();
            switch(state) {
                case CONNECTING:
                    if (DBG) log("onEnableApn: 'CONNECTING' so return");
                    apnContext.requestLog("onEnableApn state=CONNECTING, so return");
                    addRequestNetworkCompleteMsg(onCompleteMsg, apnType);
                    return;
                case CONNECTED:
                    if (DBG) log("onEnableApn: 'CONNECTED' so return");
                    // Don't add to local log since this is so common
                    sendRequestNetworkCompleteMsg(onCompleteMsg, true, mTransportType,
                            requestType, false);
                    return;
                case DISCONNECTING:
                    if (DBG) log("onEnableApn: 'DISCONNECTING' so return");
                    apnContext.requestLog("onEnableApn state=DISCONNECTING, so return");
                    sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
                            requestType, false);
                    if (DBG) log("onEnableApn: APN in " + state + " state. Exit now.");
                    if (onHandoverCompleteMsg != null) {
                        sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType,
                                false);
                    }
                    return;
                case IDLE:
                    // fall through: this is unexpected but if it happens cleanup and try setup
                case FAILED:
                case RETRYING:
                    // We're "READY" but not active so disconnect (cleanup = true) and
@@ -2664,12 +2700,7 @@ public class DcTracker extends Handler {
        apnContext.resetErrorCodeRetries();

        if (mConfigReady || apnContext.getApnTypeBitmask() == ApnSetting.TYPE_EMERGENCY) {
            if (trySetupData(apnContext, requestType)) {
                addRequestNetworkCompleteMsg(onCompleteMsg, apnType);
            } else {
                sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
                        requestType, false);
            }
            trySetupData(apnContext, requestType, onHandoverCompleteMsg);
        } else {
            log("onEnableApn: config not ready yet.");
        }
@@ -2960,16 +2991,9 @@ public class DcTracker extends Handler {
                    + DataCallResponse.failureModeToString(handoverFailureMode));
        } else if (handoverFailureMode
                != DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER) {
            int apnType = ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType());
            List<Message> messageList = mRequestNetworkCompletionMsgs.get(apnType);
            if (messageList != null) {
                for (Message msg : messageList) {
                    sendRequestNetworkCompleteMsg(msg, success, mTransportType, requestType,
            sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), success,
                    fallbackOnFailedHandover);
        }
                messageList.clear();
            }
        }

        if (success) {
            DataConnection dataConnection = apnContext.getDataConnection();
@@ -3185,6 +3209,9 @@ public class DcTracker extends Handler {
            apnContext.setDataConnection(null);
            log("onDataSetupCompleteError: Stop retrying APNs. delay=" + delay
                    + ", requestType=" + requestTypeToString(requestType));
            //send request network complete messages as needed
            sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false,
                    fallbackOnFailedHandover);
        }
    }

@@ -3253,12 +3280,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);
@@ -3742,7 +3777,7 @@ public class DcTracker extends Handler {
                    apnContext = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
                    if (apnContext != null) {
                        apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
                        trySetupData(apnContext, REQUEST_TYPE_NORMAL);
                        trySetupData(apnContext, REQUEST_TYPE_NORMAL, null);
                    } else {
                        loge("**** Default ApnContext not found ****");
                        if (TelephonyUtils.IS_DEBUGGABLE) {
@@ -3753,9 +3788,10 @@ public class DcTracker extends Handler {
                break;

            case DctConstants.EVENT_TRY_SETUP_DATA:
                trySetupData((ApnContext) msg.obj, msg.arg1);
                apnContext = (ApnContext) msg.obj;
                requestType = msg.arg1;
                trySetupData(apnContext, requestType, null);
                break;

            case DctConstants.EVENT_CLEAN_UP_CONNECTION:
                if (DBG) log("EVENT_CLEAN_UP_CONNECTION");
                cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, (ApnContext) msg.obj);
+25 −20
Original line number Diff line number Diff line
@@ -185,8 +185,6 @@ public class TelephonyNetworkFactory extends NetworkFactory {
                }
                case EVENT_DATA_HANDOVER_COMPLETED: {
                    Bundle bundle = msg.getData();
                    int requestType = bundle.getInt(DcTracker.DATA_COMPLETE_MSG_EXTRA_REQUEST_TYPE);
                    if (requestType == DcTracker.REQUEST_TYPE_HANDOVER) {
                    NetworkRequest nr = bundle.getParcelable(
                            DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST);
                    boolean success = bundle.getBoolean(
@@ -202,7 +200,6 @@ public class TelephonyNetworkFactory extends NetworkFactory {
                    } else {
                        logl("Handover completed but cannot find handover entry!");
                    }
                    }
                    break;
                }
            }
@@ -214,12 +211,20 @@ public class TelephonyNetworkFactory extends NetworkFactory {
        return mTransportManager.getCurrentTransport(apnType);
    }

    /**
     * Request network
     *
     * @param networkRequest Network request from clients
     * @param requestType The request type
     * @param transport Transport type
     * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
     * handover is completed. For normal request, this should be null.
     */
    private void requestNetworkInternal(NetworkRequest networkRequest,
                                        @RequestNetworkType int requestType,
                                        int transport, Message onCompleteMsg) {
            @RequestNetworkType int requestType, int transport, Message onHandoverCompleteMsg) {
        if (mPhone.getDcTracker(transport) != null) {
            mPhone.getDcTracker(transport).requestNetwork(networkRequest, requestType,
                    onCompleteMsg);
                    onHandoverCompleteMsg);
        }
    }

+55 −0
Original line number Diff line number Diff line
@@ -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;

@@ -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;

@@ -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;

@@ -805,6 +808,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("");

@@ -2610,4 +2625,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());
    }
}