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

Commit d63f8267 authored by Jack Yu's avatar Jack Yu
Browse files

Added handover support in the factory

Test: Manual
Bug: 73659459

Merged-In: Ie94ae1b0b2139d01578024f7217f22edf975ce9d
Change-Id: Ie94ae1b0b2139d01578024f7217f22edf975ce9d
(cherry picked from commit 7aff3ba8)
parent 6327115d
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;
+11 −13
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;
@@ -413,15 +414,13 @@ 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.
@@ -429,7 +428,6 @@ public class ApnContext {
            }
        }
    }
    }

    public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type,
                               LocalLog log) {
+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:
+130 −41

File changed.

Preview size limit exceeded, changes collapsed.

Loading