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

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

Merge changes from topic "iwlan_handover"

* changes:
  Added handover support in the factory
  Dynamically calculate network score.
parents 29da5b3c d63f8267
Loading
Loading
Loading
Loading
+121 −15
Original line number Diff line number Diff line
@@ -21,10 +21,12 @@ import android.annotation.NonNull;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.os.RegistrantList;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.ApnType;

import com.android.internal.telephony.Phone;
@@ -51,6 +53,7 @@ import java.util.stream.Collectors;
public class TransportManager extends Handler {
    private static final String TAG = TransportManager.class.getSimpleName();

    // Key is the access network, value is the transport.
    private static final Map<Integer, Integer> ACCESS_NETWORK_TRANSPORT_TYPE_MAP;

    static {
@@ -108,6 +111,26 @@ public class TransportManager extends Handler {
     */
    private final Map<Integer, int[]> mCurrentAvailableNetworks;

    /**
     * The current transport of the APN type. The key is the APN type, and the value is the
     * transport.
     */
    private final Map<Integer, Integer> mCurrentTransports;

    /**
     * The registrants for listening data handover needed events.
     */
    private final RegistrantList mHandoverNeededEventRegistrants;

    static final class HandoverParams {
        public final @ApnType int apnType;
        public final int targetTransport;
        HandoverParams(int apnType, int targetTransport) {
            this.apnType = apnType;
            this.targetTransport = targetTransport;
        }
    }

    public TransportManager(Phone phone) {
        mPhone = phone;
        mAccessNetworksManager = new AccessNetworksManager(phone);
@@ -116,6 +139,8 @@ public class TransportManager extends Handler {
                EVENT_QUALIFIED_NETWORKS_CHANGED);

        mCurrentAvailableNetworks = new ConcurrentHashMap<>();
        mCurrentTransports = new ConcurrentHashMap<>();
        mHandoverNeededEventRegistrants = new RegistrantList();

        if (isInLegacyMode()) {
            // For legacy mode, WWAN is the only transport to handle all data connections, even
@@ -140,10 +165,81 @@ public class TransportManager extends Handler {
        }
    }

    private boolean isHandoverNeeded(QualifiedNetworks newNetworks) {
        int apnType = newNetworks.apnType;
        int[] newNetworkList = newNetworks.qualifiedNetworks;
        int[] currentNetworkList = mCurrentAvailableNetworks.get(apnType);

        // If the current network list is empty, but the new network list is not, then we can
        // directly setup data on the new network. If the current network list is not empty, but
        // the new network is, then we can tear down the data directly. Therefore if one of the
        // list is empty, then we don't need to do handover.
        if (ArrayUtils.isEmpty(newNetworkList) || ArrayUtils.isEmpty(currentNetworkList)) {
            return false;
        }

        // The list is networks in the preferred order. For now we only pick the first element
        // because it's the most preferred. In the future we should also consider the rest in the
        // list, for example, the first one violates carrier/user policy.
        return !ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(newNetworkList[0]).equals(
                ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(currentNetworkList[0]));
    }

    private static boolean areNetworksValid(QualifiedNetworks networks) {
        if (networks.qualifiedNetworks == null) {
            return false;
        }
        for (int network : networks.qualifiedNetworks) {
            if (!ACCESS_NETWORK_TRANSPORT_TYPE_MAP.containsKey(network)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Set the current transport of apn type.
     *
     * @apnType The APN type
     * @transport The transport. Must be WWAN or WLAN.
     */
    public void setCurrentTransport(@ApnType int apnType, int transport) {
        mCurrentTransports.put(apnType, transport);
    }

    private synchronized void updateAvailableNetworks(List<QualifiedNetworks> networksList) {
        log("updateAvailableNetworks: " + networksList);
        for (QualifiedNetworks networks : networksList) {
            if (areNetworksValid(networks)) {
                mCurrentAvailableNetworks.put(networks.apnType, networks.qualifiedNetworks);
                if (isHandoverNeeded(networks)) {
                    // If handover is needed, perform the handover works. For now we only pick the
                    // first element because it's the most preferred. In the future we should also
                    // consider the rest in the list, for example, the first one violates
                    // carrier/user policy.
                    int targetTransport = ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(
                            networks.qualifiedNetworks[0]);
                    mHandoverNeededEventRegistrants.notifyResult(
                            new HandoverParams(networks.apnType, targetTransport));

                } else {
                    // If handover is not needed, immediately update the available networks and
                    // transport.
                    log("Handover not needed for APN type: "
                            + ApnSetting.getApnTypeString(networks.apnType));
                    int transport = TransportType.WWAN;
                    if (!ArrayUtils.isEmpty(networks.qualifiedNetworks)
                            && ACCESS_NETWORK_TRANSPORT_TYPE_MAP.containsKey(
                                    networks.qualifiedNetworks[0])) {
                        // For now we only pick the first element because it's the most preferred.
                        // In the future we should also consider the rest in the list, for example,
                        // the first one violates carrier/user policy.
                        transport = ACCESS_NETWORK_TRANSPORT_TYPE_MAP
                                .get(networks.qualifiedNetworks[0]);
                    }
                    setCurrentTransport(networks.apnType, transport);
                }
            }
        }
    }

@@ -175,7 +271,7 @@ public class TransportManager extends Handler {
    }

    /**
     * Get the transport based on the APN type
     * Get the transport based on the APN type.
     *
     * @param apnType APN type
     * @return The transport type
@@ -186,22 +282,30 @@ public class TransportManager extends Handler {
            return TransportType.WWAN;
        }

        // If we can't find the available networks, always route to cellular.
        if (!mCurrentAvailableNetworks.containsKey(apnType)) {
            return TransportType.WWAN;
        // If we can't find the corresponding transport, always route to cellular.
        return mCurrentTransports.get(apnType) == null
                ? TransportType.WWAN : mCurrentTransports.get(apnType);
    }

        int[] availableNetworks = mCurrentAvailableNetworks.get(apnType);

        // If the available networks list is empty, route to cellular.
        if (ArrayUtils.isEmpty(availableNetworks)) {
            return TransportType.WWAN;
    /**
     * Register for data handover needed event
     *
     * @param h The handler of the event
     * @param what The id of the event
     */
    public void registerForHandoverNeededEvent(Handler h, int what) {
        if (h != null) {
            mHandoverNeededEventRegistrants.addUnique(h, what, null);
        }
    }

        // TODO: For now we choose the first network because it's the most preferred one. In the
        // the future we should validate it to make sure this network does not violate carrier
        // preference and user preference.
        return ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(availableNetworks[0]);
    /**
     * Unregister for data handover needed event
     *
     * @param h The handler
     */
    public void unregisterForHandoverNeededEvent(Handler h) {
        mHandoverNeededEventRegistrants.remove(h);
    }

    /**
@@ -218,6 +322,8 @@ public class TransportManager extends Handler {
        pw.println("mAvailableTransports=[" + Arrays.stream(mAvailableTransports)
                .mapToObj(type -> TransportType.toString(type))
                .collect(Collectors.joining(",")) + "]");
        pw.println("mCurrentAvailableNetworks=" + mCurrentAvailableNetworks);
        pw.println("mCurrentTransports=" + mCurrentTransports);
        pw.println("isInLegacy=" + isInLegacyMode());
        pw.println("IWLAN operation mode=" + mPhone.mCi.getIwlanOperationMode());
        mAccessNetworksManager.dump(fd, pw, args);
+1 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ public class AccessNetworksManager {
     */
    public static class QualifiedNetworks {
        public final @ApnType int apnType;
        // The qualified netowrks in preferred order. Each network is a AccessNetworkType.
        public final int[] qualifiedNetworks;
        public QualifiedNetworks(@ApnType int apnType, int[] qualifiedNetworks) {
            this.apnType = apnType;
+23 −9
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkRequest;
import android.os.Message;
import android.telephony.Rlog;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.ApnType;
@@ -39,6 +40,7 @@ import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

@@ -146,7 +148,6 @@ public class ApnContext {
        return mDataConnection;
    }


    /**
     * Set the associated data connection.
     * @param dc data connection
@@ -413,15 +414,17 @@ public class ApnContext {
        }
    }

    public void requestNetwork(NetworkRequest networkRequest,
                               @RequestNetworkType int type, LocalLog log) {
    public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
                               Message onCompleteMsg, LocalLog log) {
        synchronized (mRefCountLock) {
            if (mLocalLogs.contains(log) || mNetworkRequests.contains(networkRequest)) {
                log.log("ApnContext.requestNetwork has duplicate add - " + mNetworkRequests.size());
            } else {
            mLocalLogs.add(log);
            mNetworkRequests.add(networkRequest);
                mDcTracker.enableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type);
            mDcTracker.enableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type,
                    onCompleteMsg);
            if (mDataConnection != null) {
                // New network request added. Should re-evaluate properties of
                // the data connection. For example, the score may change.
                mDataConnection.reevaluateDataConnectionProperties();
            }
        }
    }
@@ -439,6 +442,11 @@ public class ApnContext {
                        + networkRequest + ")");
            } else {
                mNetworkRequests.remove(networkRequest);
                if (mDataConnection != null) {
                    // New network request added. Should re-evaluate properties of
                    // the data connection. For example, the score may change.
                    mDataConnection.reevaluateDataConnectionProperties();
                }
                log.log("ApnContext.releaseNetwork left with " + mNetworkRequests.size() +
                        " requests.");
                if (mNetworkRequests.size() == 0
@@ -630,6 +638,12 @@ public class ApnContext {
        return apnType;
    }

    public List<NetworkRequest> getNetworkRequests() {
        synchronized (mRefCountLock) {
            return new ArrayList<NetworkRequest>(mNetworkRequests);
        }
    }

    @Override
    public synchronized String toString() {
        // We don't print mDataConnection because its recursive.
+68 −2
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkMisc;
import android.net.NetworkRequest;
import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.RouteInfo;
@@ -108,6 +109,18 @@ public class DataConnection extends StateMachine {

    private static final String NETWORK_TYPE = "MOBILE";

    // The data connection providing default Internet connection will have a higher score of 50.
    // Other connections will have a slightly lower score of 45. The intention is other connections
    // will not cause ConnectivityService to tear down default internet connection. For example,
    // to validate Internet connection on non-default data SIM, we'll set up a temporary Internet
    // connection on that data SIM. In this case, score of 45 is assigned so ConnectivityService
    // will not replace the default Internet connection with it.
    private static final int DEFAULT_INTERNET_CONNECTION_SCORE = 50;
    private static final int OTHER_CONNECTION_SCORE = 45;

    // The score we report to connectivity service
    private int mScore;

    // The data connection controller
    private DcController mDcController;

@@ -240,8 +253,10 @@ public class DataConnection extends StateMachine {
    static final int EVENT_LINK_CAPACITY_CHANGED = BASE + 23;
    static final int EVENT_RESET = BASE + 24;
    static final int EVENT_REEVALUATE_RESTRICTED_STATE = BASE + 25;
    static final int EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES = BASE + 26;

    private static final int CMD_TO_STRING_COUNT = EVENT_REEVALUATE_RESTRICTED_STATE - BASE + 1;
    private static final int CMD_TO_STRING_COUNT =
            EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES - BASE + 1;

    private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
    static {
@@ -275,6 +290,8 @@ public class DataConnection extends StateMachine {
        sCmdToString[EVENT_RESET - BASE] = "EVENT_RESET";
        sCmdToString[EVENT_REEVALUATE_RESTRICTED_STATE - BASE] =
                "EVENT_REEVALUATE_RESTRICTED_STATE";
        sCmdToString[EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES - BASE] =
                "EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES";
    }
    // Convert cmd to string or null if unknown
    static String cmdToString(int cmd) {
@@ -1814,9 +1831,10 @@ public class DataConnection extends StateMachine {
                    loge("Cannot find the data connection for handover.");
                }
            } else {
                mScore = calculateScore();
                mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
                        "DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties,
                        50, misc);
                        mScore, misc);
            }
            if (mDataServiceManager.getTransportType() == TransportType.WWAN) {
                mPhone.mCi.registerForNattKeepaliveStatus(
@@ -2123,6 +2141,12 @@ public class DataConnection extends StateMachine {
                    retVal = HANDLED;
                    break;
                }
                case EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES: {
                    // Update other properties like link properties if needed in future.
                    updateScore();
                    retVal = HANDLED;
                    break;
                }
                default:
                    if (VDBG) {
                        log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
@@ -2536,6 +2560,15 @@ public class DataConnection extends StateMachine {
        if (DBG) log("reevaluate restricted state");
    }

    /**
     * Re-evaluate the data connection properties. For example, it will recalculate data connection
     * score and update through network agent it if changed.
     */
    void reevaluateDataConnectionProperties() {
        sendMessage(EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES);
        if (DBG) log("reevaluate data connection properties");
    }

    /**
     * @return The parameters used for initiating a data connection.
     */
@@ -2743,6 +2776,38 @@ public class DataConnection extends StateMachine {
        }, null);
    }

    /**
     *  Re-calculate score and update through network agent if it changes.
     */
    private void updateScore() {
        int oldScore = mScore;
        mScore = calculateScore();
        if (oldScore != mScore) {
            log("Updating score from " + oldScore + " to " + mScore);
            mNetworkAgent.sendNetworkScore(mScore);
        }
    }

    private int calculateScore() {
        int score = OTHER_CONNECTION_SCORE;

        // If it's serving a network request that asks NET_CAPABILITY_INTERNET and doesn't have
        // specify a subId, this dataConnection is considered to be default Internet data
        // connection. In this case we assign a slightly higher score of 50. The intention is
        // it will not be replaced by other data connections accidentally in DSDS usecase.
        for (ApnContext apnContext : mApnContexts.keySet()) {
            for (NetworkRequest networkRequest : apnContext.getNetworkRequests()) {
                if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                        && networkRequest.networkCapabilities.getNetworkSpecifier() == null) {
                    score = DEFAULT_INTERNET_CONNECTION_SCORE;
                    break;
                }
            }
        }

        return score;
    }

    /**
     * Dump the current state.
     *
@@ -2781,6 +2846,7 @@ public class DataConnection extends StateMachine {
        pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly);
        pw.println("mInstanceNumber=" + mInstanceNumber);
        pw.println("mAc=" + mAc);
        pw.println("mScore=" + mScore);
        pw.println("Network capabilities changed history:");
        pw.increaseIndent();
        mNetCapsLocalLog.dump(fd, pw, args);
+77 −17
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.PriorityQueue;
import java.util.Set;
@@ -180,6 +181,12 @@ public class DcTracker extends Handler {
     */
    public static final int RELEASE_TYPE_HANDOVER = 3;

    /** The extras for request network 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";

    private final String mLogTag;

    public AtomicBoolean isCleanupRequired = new AtomicBoolean(false);
@@ -627,6 +634,13 @@ public class DcTracker extends Handler {

    private DataStallRecoveryHandler mDsRecoveryHandler;

    /**
     * Request network completion message map. Key is the APN type, value is the list of completion
     * 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<>();

    //***** Constructor
    public DcTracker(Phone phone, int transportType) {
        super();
@@ -852,13 +866,13 @@ public class DcTracker extends Handler {
    }

    public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
                               LocalLog log) {
                               Message onCompleteMsg, LocalLog log) {
        final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
        final ApnContext apnContext = mApnContextsByType.get(apnType);
        log.log("DcTracker.requestNetwork for " + networkRequest + " found " + apnContext
                + ", type=" + requestTypeToString(type));
        if (apnContext != null) {
            apnContext.requestNetwork(networkRequest, type, log);
            apnContext.requestNetwork(networkRequest, type, onCompleteMsg, log);
        }
    }

@@ -2268,20 +2282,44 @@ public class DcTracker extends Handler {
        return null;
    }

    public void enableApn(@ApnSetting.ApnType int apnType, @RequestNetworkType int requestType) {
        sendMessage(obtainMessage(DctConstants.EVENT_ENABLE_APN, apnType, requestType));
    private void addRequestNetworkCompleteMsg(Message onCompleteMsg,
                                              @ApnSetting.ApnType int apnType) {
        if (onCompleteMsg != null) {
            List<Message> messageList = mRequestNetworkCompletionMsgs.get(apnType);
            if (messageList == null) messageList = new ArrayList<>();
            messageList.add(onCompleteMsg);
            mRequestNetworkCompletionMsgs.put(apnType, messageList);
        }
    }

    private void sendRequestNetworkCompleteMsg(Message message, boolean success, int transport,
                                               @RequestNetworkType int requestType) {
        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);
        message.sendToTarget();
    }

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

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

        boolean trySetup = false;
        String str = "onEnableApn: apnType=" + ApnSetting.getApnTypeString(apnType)
                + ", request type=" + requestType;
                + ", request type=" + requestTypeToString(requestType);
        if (DBG) log(str);
        apnContext.requestLog(str);

@@ -2291,6 +2329,7 @@ public class DcTracker extends Handler {
            str = "onEnableApn: dependency is not met.";
            if (DBG) log(str);
            apnContext.requestLog(str);
            sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType);
            return;
        }

@@ -2298,11 +2337,22 @@ 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");
                    apnContext.requestLog("onEnableApn state=CONNECTED, so return");

                    sendRequestNetworkCompleteMsg(onCompleteMsg, true, mTransportType,
                            requestType);
                    return;
                case DISCONNECTING:
                    // We're "READY" and active so just return
                    if (DBG) log("onEnableApn: 'ready' so return");
                    apnContext.requestLog("onEnableApn state=" + state + ", so return");
                    if (DBG) log("onEnableApn: 'DISCONNECTING' so return");
                    apnContext.requestLog("onEnableApn state=DISCONNECTING, so return");
                    sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
                            requestType);
                    return;
                case IDLE:
                    // fall through: this is unexpected but if it happens cleanup and try setup
@@ -2310,7 +2360,6 @@ public class DcTracker extends Handler {
                case RETRYING:
                    // We're "READY" but not active so disconnect (cleanup = true) and
                    // connect (trySetup = true) to be sure we retry the connection.
                    trySetup = true;
                    apnContext.setReason(Phone.REASON_DATA_ENABLED);
                    break;
            }
@@ -2323,12 +2372,14 @@ public class DcTracker extends Handler {
            if (apnContext.getState() == DctConstants.State.FAILED) {
                apnContext.setState(DctConstants.State.IDLE);
            }
            trySetup = true;
        }
        apnContext.setEnabled(true);
        if (trySetup) {
        apnContext.resetErrorCodeRetries();
            trySetupData(apnContext, requestType);
        if (trySetupData(apnContext, requestType)) {
            addRequestNetworkCompleteMsg(onCompleteMsg, apnType);
        } else {
            sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
                    requestType);
        }
    }

@@ -2346,7 +2397,7 @@ public class DcTracker extends Handler {

        boolean cleanup = false;
        String str = "onDisableApn: apnType=" + ApnSetting.getApnTypeString(apnType)
                + ", release type=" + releaseType;
                + ", release type=" + releaseTypeToString(releaseType);
        if (DBG) log(str);
        apnContext.requestLog(str);

@@ -2669,6 +2720,15 @@ public class DcTracker extends Handler {
     */
    private void onDataSetupComplete(ApnContext apnContext, boolean success, int cause,
                                     @RequestNetworkType int requestType) {
        int apnType = ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType());
        List<Message> messageList = mRequestNetworkCompletionMsgs.get(apnType);
        if (messageList != null) {
            for (Message msg : messageList) {
                sendRequestNetworkCompleteMsg(msg, success, mTransportType, requestType);
            }
            messageList.clear();
        }

        if (success) {
            DataConnection dataConnection = apnContext.getDataConnection();

@@ -3509,7 +3569,7 @@ public class DcTracker extends Handler {
                break;

            case DctConstants.EVENT_ENABLE_APN:
                onEnableApn(msg.arg1, msg.arg2);
                onEnableApn(msg.arg1, msg.arg2, (Message) msg.obj);
                break;

            case DctConstants.EVENT_DISABLE_APN:
Loading