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

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

Added IWLAN handover policy support

For some certain scenarios, IWLAN handover is
allowed or disallowed.

Fix: 213507287
Test: atest DataNetworkControllerTest
Merged-In: Iaf2df7f7f4a8661ad251335738e49766278544ad
Change-Id: Iaf2df7f7f4a8661ad251335738e49766278544ad
parent c55b1567
Loading
Loading
Loading
Loading
+5 −5
Original line number Original line Diff line number Diff line
@@ -656,16 +656,16 @@ public class AccessNetworksManager extends Handler {
        for (QualifiedNetworks networks : networksList) {
        for (QualifiedNetworks networks : networksList) {
            if (networks.qualifiedNetworks.length > 0) {
            if (networks.qualifiedNetworks.length > 0) {
                int transport = getTransportFromAccessNetwork(networks.qualifiedNetworks[0]);
                int transport = getTransportFromAccessNetwork(networks.qualifiedNetworks[0]);
                if (getCurrentTransport(networks.apnType) != transport) {
                if (getPreferredTransport(networks.apnType) != transport) {
                    mAccessNetworksManagerCallbacks.forEach(callback ->
                    mAccessNetworksManagerCallbacks.forEach(callback ->
                            callback.invokeFromExecutor(() ->
                            callback.invokeFromExecutor(() ->
                                    callback.onPreferredTransportChanged(DataUtils
                                    callback.onPreferredTransportChanged(DataUtils
                                            .apnTypeToNetworkCapability(networks.apnType))));
                                            .apnTypeToNetworkCapability(networks.apnType))));
                }
                    mPreferredTransports.put(networks.apnType, transport);
                    mPreferredTransports.put(networks.apnType, transport);
                    logl("setPreferredTransports: apnType="
                    logl("setPreferredTransports: apnType="
                        + ApnSetting.getApnTypeString(networks.apnType)
                            + ApnSetting.getApnTypeString(networks.apnType) + ", transport="
                        + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
                            + AccessNetworkConstants.transportTypeToString(transport));
                }
            }
            }
        }
        }
    }
    }
+22 −8
Original line number Original line Diff line number Diff line
@@ -21,14 +21,17 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.telephony.data.DataProfile;
import android.telephony.data.DataProfile;


import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashSet;
import java.util.List;
import java.util.List;
import java.util.Set;
import java.util.Set;


/**
/**
 * The class to describe a data evaluation for whether allowing or disallowing
 * The class to describe a data evaluation for whether allowing or disallowing certain operations
 * establishing a data network.
 * like setup a data network, sustaining existing data networks, or handover between IWLAN and
 * cellular.
 */
 */
public class DataEvaluation {
public class DataEvaluation {
    /** The reason for this evaluation */
    /** The reason for this evaluation */
@@ -107,7 +110,7 @@ public class DataEvaluation {
    }
    }


    /**
    /**
     * @return {@code true} if data is allowed.
     * @return {@code true} if the operation is allowed.
     */
     */
    public boolean isDataAllowed() {
    public boolean isDataAllowed() {
        return mDataDisallowedReasons.size() == 0;
        return mDataDisallowedReasons.size() == 0;
@@ -155,8 +158,11 @@ public class DataEvaluation {
        return false;
        return false;
    }
    }


    /** The reason for evaluating unsatisfied network requests. */
    /**
    enum DataEvaluationReason {
     * The reason for evaluating unsatisfied network requests, existing data networks, and handover.
     */
    @VisibleForTesting
    public enum DataEvaluationReason {
        /** New request from the apps. */
        /** New request from the apps. */
        NEW_REQUEST,
        NEW_REQUEST,
        /** Data config changed. */
        /** Data config changed. */
@@ -185,9 +191,13 @@ public class DataEvaluation {
        RETRY_AFTER_DISCONNECTED,
        RETRY_AFTER_DISCONNECTED,
        /** Data setup retry. */
        /** Data setup retry. */
        DATA_RETRY,
        DATA_RETRY,
        /** Handover between IWLAN and cellular. */
        DATA_HANDOVER,
        /** Preferred transport changed. */
        PREFERRED_TRANSPORT_CHANGED,
    }
    }


    /** Disallowed reasons. There could be multiple reasons if data connection is not allowed. */
    /** Disallowed reasons. There could be multiple reasons if it is not allowed. */
    public enum DataDisallowedReason {
    public enum DataDisallowedReason {
        // Soft failure reasons. A soft reason means that in certain conditions, data is still
        // Soft failure reasons. A soft reason means that in certain conditions, data is still
        // allowed. Normally those reasons are due to users settings.
        // allowed. Normally those reasons are due to users settings.
@@ -222,14 +232,18 @@ public class DataEvaluation {
        DATA_NETWORK_TYPE_NOT_ALLOWED(true),
        DATA_NETWORK_TYPE_NOT_ALLOWED(true),
        /** Device is currently in an emergency call. */
        /** Device is currently in an emergency call. */
        EMERGENCY_CALL(true),
        EMERGENCY_CALL(true),
        /** There is already a retry setup scheduled for this data profile. */
        /** There is already a retry setup/handover scheduled. */
        RETRY_SCHEDULED(true),
        RETRY_SCHEDULED(true),
        /** Network has explicitly request to throttle setup attempt. */
        /** Network has explicitly request to throttle setup attempt. */
        DATA_THROTTLED(true),
        DATA_THROTTLED(true),
        /** Data profile becomes invalid. (could be removed by the user, or SIM refresh, etc..) */
        /** Data profile becomes invalid. (could be removed by the user, or SIM refresh, etc..) */
        DATA_PROFILE_INVALID(true),
        DATA_PROFILE_INVALID(true),
        /** Data profile not preferred (i.e. users switch preferred profile in APN editor.) */
        /** Data profile not preferred (i.e. users switch preferred profile in APN editor.) */
        DATA_PROFILE_NOT_PREFERRED(true);
        DATA_PROFILE_NOT_PREFERRED(true),
        /** Handover is not allowed by policy. */
        NOT_ALLOWED_BY_POLICY(true),
        /** Data network is not in the right state. */
        ILLEGAL_STATE(true);


        private final boolean mIsHardReason;
        private final boolean mIsHardReason;


+6 −0
Original line number Original line Diff line number Diff line
@@ -212,6 +212,7 @@ public class DataNetwork extends StateMachine {
                    TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER,
                    TEAR_DOWN_REASON_POWER_OFF_BY_CARRIER,
                    TEAR_DOWN_REASON_DATA_STALL,
                    TEAR_DOWN_REASON_DATA_STALL,
                    TEAR_DOWN_REASON_HANDOVER_FAILED,
                    TEAR_DOWN_REASON_HANDOVER_FAILED,
                    TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED,
            })
            })
    public @interface TearDownReason {}
    public @interface TearDownReason {}


@@ -254,6 +255,9 @@ public class DataNetwork extends StateMachine {
    /** Data network tear down due to handover failed. */
    /** Data network tear down due to handover failed. */
    public static final int TEAR_DOWN_REASON_HANDOVER_FAILED = 13;
    public static final int TEAR_DOWN_REASON_HANDOVER_FAILED = 13;


    /** Data network tear down due to handover not allowed. */
    public static final int TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED = 14;

    @IntDef(prefix = {"BANDWIDTH_SOURCE_"},
    @IntDef(prefix = {"BANDWIDTH_SOURCE_"},
            value = {
            value = {
                    BANDWIDTH_SOURCE_UNKNOWN,
                    BANDWIDTH_SOURCE_UNKNOWN,
@@ -2192,6 +2196,8 @@ public class DataNetwork extends StateMachine {
                return "TEAR_DOWN_REASON_DATA_STALL";
                return "TEAR_DOWN_REASON_DATA_STALL";
            case TEAR_DOWN_REASON_HANDOVER_FAILED:
            case TEAR_DOWN_REASON_HANDOVER_FAILED:
                return "TEAR_DOWN_REASON_HANDOVER_FAILED";
                return "TEAR_DOWN_REASON_HANDOVER_FAILED";
            case TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED:
                return "TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED";
            default:
            default:
                return "UNKNOWN(" + reason + ")";
                return "UNKNOWN(" + reason + ")";
        }
        }
+93 −29
Original line number Original line Diff line number Diff line
@@ -176,8 +176,8 @@ public class DataNetworkController extends Handler {
    /** Event for emergency call started or ended. */
    /** Event for emergency call started or ended. */
    private static final int EVENT_EMERGENCY_CALL_CHANGED = 20;
    private static final int EVENT_EMERGENCY_CALL_CHANGED = 20;


    /** Event for re-evaluating preferred transport. */
    /** Event for preferred transport changed. */
    private static final int EVENT_REEVALUATE_PREFERRED_TRANSPORT = 21;
    private static final int EVENT_PREFERRED_TRANSPORT_CHANGED = 21;


    /** Event for subscription plans changed. */
    /** Event for subscription plans changed. */
    private static final int EVENT_SUBSCRIPTION_PLANS_CHANGED = 22;
    private static final int EVENT_SUBSCRIPTION_PLANS_CHANGED = 22;
@@ -551,7 +551,7 @@ public class DataNetworkController extends Handler {
        private static final String RULE_TAG_ROAMING = "roaming";
        private static final String RULE_TAG_ROAMING = "roaming";


        /** Handover rule type. */
        /** Handover rule type. */
        public final @HandoverRuleType int ruleType;
        public final @HandoverRuleType int type;


        /** The applicable source access networks for handover. */
        /** The applicable source access networks for handover. */
        public final @NonNull @RadioAccessNetworkType Set<Integer> sourceAccessNetworks;
        public final @NonNull @RadioAccessNetworkType Set<Integer> sourceAccessNetworks;
@@ -560,7 +560,7 @@ public class DataNetworkController extends Handler {
        public final @NonNull @RadioAccessNetworkType Set<Integer> targetAccessNetworks;
        public final @NonNull @RadioAccessNetworkType Set<Integer> targetAccessNetworks;


        /** {@code true} indicates this policy is only applicable when the device is roaming. */
        /** {@code true} indicates this policy is only applicable when the device is roaming. */
        public final boolean isRoaming;
        public final boolean isOnlyForRoaming;


        /**
        /**
         * Constructor
         * Constructor
@@ -652,17 +652,17 @@ public class DataNetworkController extends Handler {


            sourceAccessNetworks = source;
            sourceAccessNetworks = source;
            targetAccessNetworks = target;
            targetAccessNetworks = target;
            ruleType = type;
            this.type = type;
            isRoaming = roaming;
            isOnlyForRoaming = roaming;
        }
        }


        @Override
        @Override
        public String toString() {
        public String toString() {
            return "[HandoverRule: type=" + (ruleType == RULE_TYPE_ALLOWED ? "allowed"
            return "[HandoverRule: type=" + (type == RULE_TYPE_ALLOWED ? "allowed"
                    : "disallowed") + ", source=" + sourceAccessNetworks.stream()
                    : "disallowed") + ", source=" + sourceAccessNetworks.stream()
                    .map(AccessNetworkType::toString).collect(Collectors.joining("|"))
                    .map(AccessNetworkType::toString).collect(Collectors.joining("|"))
                    + ", target=" + targetAccessNetworks.stream().map(AccessNetworkType::toString)
                    + ", target=" + targetAccessNetworks.stream().map(AccessNetworkType::toString)
                    .collect(Collectors.joining("|")) + ", isRoaming=" + isRoaming + "]";
                    .collect(Collectors.joining("|")) + ", isRoaming=" + isOnlyForRoaming + "]";
        }
        }
    }
    }


@@ -783,7 +783,9 @@ public class DataNetworkController extends Handler {
        mAccessNetworksManager.registerCallback(new AccessNetworksManagerCallback(this::post) {
        mAccessNetworksManager.registerCallback(new AccessNetworksManagerCallback(this::post) {
            @Override
            @Override
            public void onPreferredTransportChanged(@NetCapability int capability) {
            public void onPreferredTransportChanged(@NetCapability int capability) {
                DataNetworkController.this.onReevaluatePreferredTransport(capability);
                DataNetworkController.this.onPreferredTransportChanged(capability);
                sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS,
                        DataEvaluationReason.PREFERRED_TRANSPORT_CHANGED));
            }
            }
        });
        });


@@ -919,8 +921,8 @@ public class DataNetworkController extends Handler {
                            DataEvaluationReason.EMERGENCY_CALL_CHANGED));
                            DataEvaluationReason.EMERGENCY_CALL_CHANGED));
                }
                }
                break;
                break;
            case EVENT_REEVALUATE_PREFERRED_TRANSPORT:
            case EVENT_PREFERRED_TRANSPORT_CHANGED:
                onReevaluatePreferredTransport(msg.arg1);
                onPreferredTransportChanged(msg.arg1);
                break;
                break;
            case EVENT_SUBSCRIPTION_PLANS_CHANGED:
            case EVENT_SUBSCRIPTION_PLANS_CHANGED:
                SubscriptionPlan[] plans = (SubscriptionPlan[]) msg.obj;
                SubscriptionPlan[] plans = (SubscriptionPlan[]) msg.obj;
@@ -1424,6 +1426,67 @@ public class DataNetworkController extends Handler {
        }
        }
    }
    }


    /**
     * Evaluate if it is allowed to handover the data network between IWLAN and cellular. Some
     * carriers do not allow handover in certain conditions.
     *
     * @param dataNetwork The data network to be handover.
     * @return The evaluation result.
     *
     * @see CarrierConfigManager#KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY
     */
    private @NonNull DataEvaluation evaluateDataNetworkHandover(@NonNull DataNetwork dataNetwork) {
        DataEvaluation dataEvaluation = new DataEvaluation(DataEvaluationReason.DATA_HANDOVER);
        if (!dataNetwork.isConnecting() && !dataNetwork.isConnected()) {
            log("evaluateDataNetworkHandover:" + dataNetwork
                    + " it not in the right state.");
            dataEvaluation.addDataDisallowedReason(DataDisallowedReason.ILLEGAL_STATE);
            return dataEvaluation;
        }

        if (mDataRetryManager.isAnyHandoverRetryScheduled(dataNetwork)) {
            log("evaluateDataNetworkHandover: Found handover retry entry for "
                    + dataNetwork);
            dataEvaluation.addDataDisallowedReason(DataDisallowedReason.RETRY_SCHEDULED);
            return dataEvaluation;
        }

        List<HandoverRule> handoverRules = mDataConfigManager.getHandoverRules();

        int sourceAccessNetwork = DataUtils.networkTypeToAccessNetworkType(
                getDataNetworkType(dataNetwork.getTransport()));
        int targetAccessNetwork = DataUtils.networkTypeToAccessNetworkType(
                getDataNetworkType(DataUtils.getTargetTransport(dataNetwork.getTransport())));
        log("evaluateDataNetworkHandover: "
                + "source=" + AccessNetworkType.toString(sourceAccessNetwork)
                + ", target=" + AccessNetworkType.toString(targetAccessNetwork)
                + ", ServiceState=" + mServiceState);

        // Matching the rules by the configured order. Bail out if find first matching rule.
        for (HandoverRule rule : handoverRules) {
            // Check if the rule is only for roaming and we are not roaming.
            if (rule.isOnlyForRoaming && !mServiceState.getDataRoaming()) continue;

            if (rule.sourceAccessNetworks.contains(sourceAccessNetwork)
                    && rule.targetAccessNetworks.contains(targetAccessNetwork)) {
                log("evaluateDataNetworkHandover: Matched " + rule);
                if (rule.type == HandoverRule.RULE_TYPE_DISALLOWED) {
                    dataEvaluation.addDataDisallowedReason(
                            DataDisallowedReason.NOT_ALLOWED_BY_POLICY);
                } else {
                    dataEvaluation.addDataAllowedReason(DataAllowedReason.NORMAL);
                }
                log("evaluateDataNetworkHandover: " + dataEvaluation);
                return dataEvaluation;
            }
        }

        log("evaluateDataNetworkHandover: Did not find matching rule. " + dataEvaluation);
        // Allow handover anyway if no rule is found.
        dataEvaluation.addDataAllowedReason(DataAllowedReason.NORMAL);
        return dataEvaluation;
    }

    /**
    /**
     * Get tear down reason from the evaluation result.
     * Get tear down reason from the evaluation result.
     *
     *
@@ -1688,7 +1751,7 @@ public class DataNetworkController extends Handler {
    private void setupDataNetwork(@NonNull DataProfile dataProfile,
    private void setupDataNetwork(@NonNull DataProfile dataProfile,
            @Nullable DataSetupRetryEntry dataSetupRetryEntry) {
            @Nullable DataSetupRetryEntry dataSetupRetryEntry) {
        log("onSetupDataNetwork: dataProfile=" + dataProfile + ", retryEntry="
        log("onSetupDataNetwork: dataProfile=" + dataProfile + ", retryEntry="
                + dataSetupRetryEntry);
                + dataSetupRetryEntry + ", service state=" + mServiceState);
        for (DataNetwork dataNetwork : mDataNetworkList) {
        for (DataNetwork dataNetwork : mDataNetworkList) {
            if (dataNetwork.getDataProfile().equals(dataProfile)) {
            if (dataNetwork.getDataProfile().equals(dataProfile)) {
                log("onSetupDataNetwork: Found existing data network " + dataNetwork
                log("onSetupDataNetwork: Found existing data network " + dataNetwork
@@ -2047,7 +2110,7 @@ public class DataNetworkController extends Handler {
            // to the original one, but we should re-evaluate the preferred transport again to
            // to the original one, but we should re-evaluate the preferred transport again to
            // make sure QNS does change it back, if not, we still need to perform handover at that
            // make sure QNS does change it back, if not, we still need to perform handover at that
            // time.
            // time.
            sendMessageDelayed(obtainMessage(EVENT_REEVALUATE_PREFERRED_TRANSPORT,
            sendMessageDelayed(obtainMessage(EVENT_PREFERRED_TRANSPORT_CHANGED,
                    dataNetwork.getHighestPriorityNetworkCapability(), 0),
                    dataNetwork.getHighestPriorityNetworkCapability(), 0),
                    REEVALUATE_PREFERRED_TRANSPORT_DELAY_MILLIS);
                    REEVALUATE_PREFERRED_TRANSPORT_DELAY_MILLIS);
        } else if (handoverFailureMode == DataCallResponse
        } else if (handoverFailureMode == DataCallResponse
@@ -2143,14 +2206,15 @@ public class DataNetworkController extends Handler {
    }
    }


    /**
    /**
     * Called when needed to re-evaluate preferred transport for certain capability.
     * Called when preferred transport changed for certain capability.
     *
     *
     * @param capability The network capability that has preferred transport changed.
     * @param capability The network capability that has preferred transport changed.
     */
     */
    private void onReevaluatePreferredTransport(@NetCapability int capability) {
    private void onPreferredTransportChanged(@NetCapability int capability) {
        int preferredTransport = mAccessNetworksManager
        int preferredTransport = mAccessNetworksManager
                .getPreferredTransportByNetworkCapability(capability);
                .getPreferredTransportByNetworkCapability(capability);
        logl(DataUtils.networkCapabilityToString(capability) + " preferred on "
        logl("onPreferredTransportChanged: " + DataUtils.networkCapabilityToString(capability)
                + " preferred on "
                + AccessNetworkConstants.transportTypeToString(preferredTransport));
                + AccessNetworkConstants.transportTypeToString(preferredTransport));
        for (DataNetwork dataNetwork : mDataNetworkList) {
        for (DataNetwork dataNetwork : mDataNetworkList) {
            if (dataNetwork.getHighestPriorityNetworkCapability() == capability) {
            if (dataNetwork.getHighestPriorityNetworkCapability() == capability) {
@@ -2162,20 +2226,20 @@ public class DataNetworkController extends Handler {
                    continue;
                    continue;
                }
                }


                if (!dataNetwork.isConnecting() && !dataNetwork.isConnected()) {
                DataEvaluation dataEvaluation = evaluateDataNetworkHandover(dataNetwork);
                    log("onReevaluatePreferredTransport:" + dataNetwork
                if (dataEvaluation.isDataAllowed()) {
                            + " it not in the right state.");
                    continue;
                }

                if (mDataRetryManager.isAnyHandoverRetryScheduled(dataNetwork)) {
                    log("onReevaluatePreferredTransport: Found handover retry entry for "
                            + dataNetwork);
                    continue;
                }
                    logl("Start handover " + dataNetwork + " to "
                    logl("Start handover " + dataNetwork + " to "
                            + AccessNetworkConstants.transportTypeToString(preferredTransport));
                            + AccessNetworkConstants.transportTypeToString(preferredTransport));
                    dataNetwork.startHandover(preferredTransport, null);
                    dataNetwork.startHandover(preferredTransport, null);
                } else if (dataEvaluation.containsOnly(
                        DataDisallowedReason.NOT_ALLOWED_BY_POLICY)) {
                    logl("onPreferredTransportChanged: Handover not allowed by policy. Tear "
                            + "down the network so a new network can be setup on "
                            + AccessNetworkConstants.transportTypeToString(preferredTransport)
                            + ".");
                    tearDownGracefully(dataNetwork,
                            DataNetwork.TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED);
                }
            }
            }
        }
        }
    }
    }
+124 −16

File changed.

Preview size limit exceeded, changes collapsed.