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

Commit 30a4cfec authored by gwenlin's avatar gwenlin
Browse files

Support handover rule based on incall

Some carriers do not allow in-call handover such as NR and WiFi
due to network limitation. Adding the incall rule to support it.

Flag: com.android.internal.telephony.flags.incall_handover_policy
Bug: 376765521
Test: atest DataNetworkControllerTest
Change-Id: Ia1e6b367fa212d464d692d7e6b9d8ab429a3c066
parent e9e96366
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -129,3 +129,14 @@ flag {
  bug: "366194627"
  is_exported: true
}

# OWNER=gwenlin TARGET=25Q2
flag {
  name: "incall_handover_policy"
  namespace: "telephony"
  description: "Support IWLAN handover policy based on incall."
  bug:"376765521"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}
+1 −1
Original line number Diff line number Diff line
@@ -1269,7 +1269,7 @@ public class DataConfigManager extends Handler {
            if (handoverRulesStrings != null) {
                for (String ruleString : handoverRulesStrings) {
                    try {
                        mHandoverRuleList.add(new HandoverRule(ruleString));
                        mHandoverRuleList.add(new HandoverRule(ruleString, mFeatureFlags));
                    } catch (IllegalArgumentException e) {
                        loge("updateHandoverRules: " + e.getMessage());
                    }
+24 −3
Original line number Diff line number Diff line
@@ -741,6 +741,8 @@ public class DataNetworkController extends Handler {

        private static final String RULE_TAG_ROAMING = "roaming";

        private static final String RULE_TAG_INCALL = "incall";

        /** Handover rule type. */
        @HandoverRuleType
        public final int type;
@@ -766,6 +768,9 @@ public class DataNetworkController extends Handler {
        /** {@code true} indicates this policy is only applicable when the device is roaming. */
        public final boolean isOnlyForRoaming;

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

        /**
         * Constructor
         *
@@ -773,7 +778,7 @@ public class DataNetworkController extends Handler {
         *
         * @see CarrierConfigManager#KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY
         */
        public HandoverRule(@NonNull String ruleString) {
        public HandoverRule(@NonNull String ruleString, @NonNull FeatureFlags featureFlags) {
            if (TextUtils.isEmpty(ruleString)) {
                throw new IllegalArgumentException("illegal rule " + ruleString);
            }
@@ -781,6 +786,7 @@ public class DataNetworkController extends Handler {
            Set<Integer> source = null, target = null, capabilities = Collections.emptySet();
            int type = 0;
            boolean roaming = false;
            boolean incall = false;

            ruleString = ruleString.trim().toLowerCase(Locale.ROOT);
            String[] expressions = ruleString.split("\\s*,\\s*");
@@ -821,6 +827,11 @@ public class DataNetworkController extends Handler {
                        case RULE_TAG_ROAMING:
                            roaming = Boolean.parseBoolean(value);
                            break;
                        case RULE_TAG_INCALL:
                            if (featureFlags.incallHandoverPolicy()) {
                                incall = Boolean.parseBoolean(value);
                            }
                            break;
                        default:
                            throw new IllegalArgumentException("unexpected key " + key);
                    }
@@ -867,6 +878,7 @@ public class DataNetworkController extends Handler {
            this.type = type;
            networkCapabilities = capabilities;
            isOnlyForRoaming = roaming;
            isOnlyForIncall = incall;
        }

        @Override
@@ -876,8 +888,8 @@ public class DataNetworkController extends Handler {
                    .map(AccessNetworkType::toString).collect(Collectors.joining("|"))
                    + ", target=" + targetAccessNetworks.stream().map(AccessNetworkType::toString)
                    .collect(Collectors.joining("|")) + ", isRoaming=" + isOnlyForRoaming
                    + ", capabilities=" + DataUtils.networkCapabilitiesToString(networkCapabilities)
                    + "]";
                    + ", isIncall=" + isOnlyForIncall + ", capabilities="
                    + DataUtils.networkCapabilitiesToString(networkCapabilities) + "]";
        }
    }

@@ -2337,6 +2349,9 @@ public class DataNetworkController extends Handler {
            // in data network.
            boolean isRoaming = isWwanInService ? mServiceState.getDataRoamingFromRegistration()
                    : dataNetwork.getLastKnownRoamingState();
            Phone imsPhone = mPhone.getImsPhone();
            boolean isIncall = mFeatureFlags.incallHandoverPolicy() && imsPhone != null
                    && (imsPhone.getCallTracker().getState() != PhoneConstants.State.IDLE);
            int targetAccessNetwork = DataUtils.networkTypeToAccessNetworkType(
                    getDataNetworkType(DataUtils.getTargetTransport(dataNetwork.getTransport())));
            NetworkCapabilities capabilities = dataNetwork.getNetworkCapabilities();
@@ -2344,6 +2359,7 @@ public class DataNetworkController extends Handler {
                    + "source=" + AccessNetworkType.toString(sourceAccessNetwork)
                    + ", target=" + AccessNetworkType.toString(targetAccessNetwork)
                    + ", roaming=" + isRoaming
                    + ", incall=" + isIncall
                    + ", ServiceState=" + mServiceState
                    + ", capabilities=" + capabilities);

@@ -2355,6 +2371,11 @@ public class DataNetworkController extends Handler {
                    // this rule.
                    continue;
                }
                if (rule.isOnlyForIncall && (!mFeatureFlags.incallHandoverPolicy() || !isIncall)) {
                    // If the rule is for incall only, and the device is not incall, then bypass
                    // this rule.
                    continue;
                }

                if (rule.sourceAccessNetworks.contains(sourceAccessNetwork)
                        && rule.targetAccessNetworks.contains(targetAccessNetwork)) {
+64 −16
Original line number Diff line number Diff line
@@ -890,6 +890,7 @@ public class DataNetworkControllerTest extends TelephonyTest {
                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
        doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
        doReturn(true).when(mFeatureFlags).satelliteInternet();
        doReturn(true).when(mFeatureFlags).incallHandoverPolicy();
        when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
        doReturn(true).when(mMockPackageManager).hasSystemFeature(anyString());

@@ -2375,7 +2376,7 @@ public class DataNetworkControllerTest extends TelephonyTest {
    @Test
    public void testHandoverRuleFromString() {
        HandoverRule handoverRule = new HandoverRule("source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, "
                + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed");
                + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed", mFeatureFlags);
        assertThat(handoverRule.sourceAccessNetworks).containsExactly(AccessNetworkType.GERAN,
                AccessNetworkType.UTRAN, AccessNetworkType.EUTRAN, AccessNetworkType.NGRAN,
                AccessNetworkType.IWLAN);
@@ -2387,7 +2388,7 @@ public class DataNetworkControllerTest extends TelephonyTest {
        assertThat(handoverRule.networkCapabilities).isEmpty();

        handoverRule = new HandoverRule("source=   NGRAN|     IWLAN, "
                + "target  =    EUTRAN,    type  =    disallowed ");
                + "target  =    EUTRAN,    type  =    disallowed ", mFeatureFlags);
        assertThat(handoverRule.sourceAccessNetworks).containsExactly(AccessNetworkType.NGRAN,
                AccessNetworkType.IWLAN);
        assertThat(handoverRule.targetAccessNetworks).containsExactly(AccessNetworkType.EUTRAN);
@@ -2397,7 +2398,7 @@ public class DataNetworkControllerTest extends TelephonyTest {

        handoverRule = new HandoverRule("source=   IWLAN, "
                + "target  =    EUTRAN,    type  =    disallowed, roaming = true,"
                + " capabilities = IMS | EIMS ");
                + " capabilities = IMS | EIMS ", mFeatureFlags);
        assertThat(handoverRule.sourceAccessNetworks).containsExactly(AccessNetworkType.IWLAN);
        assertThat(handoverRule.targetAccessNetworks).containsExactly(AccessNetworkType.EUTRAN);
        assertThat(handoverRule.type).isEqualTo(HandoverRule.RULE_TYPE_DISALLOWED);
@@ -2406,7 +2407,8 @@ public class DataNetworkControllerTest extends TelephonyTest {
        assertThat(handoverRule.isOnlyForRoaming).isTrue();

        handoverRule = new HandoverRule("source=EUTRAN|NGRAN|IWLAN|UNKNOWN, "
                + "target=EUTRAN|NGRAN|IWLAN, type=disallowed, capabilities = IMS|EIMS");
                + "target=EUTRAN|NGRAN|IWLAN, type=disallowed, capabilities = IMS|EIMS",
                mFeatureFlags);
        assertThat(handoverRule.sourceAccessNetworks).containsExactly(AccessNetworkType.EUTRAN,
                AccessNetworkType.NGRAN, AccessNetworkType.IWLAN, AccessNetworkType.UNKNOWN);
        assertThat(handoverRule.targetAccessNetworks).containsExactly(AccessNetworkType.EUTRAN,
@@ -2415,44 +2417,61 @@ public class DataNetworkControllerTest extends TelephonyTest {
        assertThat(handoverRule.networkCapabilities).containsExactly(
                NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_EIMS);

        handoverRule = new HandoverRule("source=NGRAN|IWLAN, "
                + "target  =    NGRAN|IWLAN,    type=disallowed, incall = true,"
                + " capabilities = IMS|EIMS ", mFeatureFlags);
        assertThat(handoverRule.sourceAccessNetworks).containsExactly(AccessNetworkType.NGRAN,
                AccessNetworkType.IWLAN);
        assertThat(handoverRule.sourceAccessNetworks).containsExactly(AccessNetworkType.NGRAN,
                AccessNetworkType.IWLAN);
        assertThat(handoverRule.type).isEqualTo(HandoverRule.RULE_TYPE_DISALLOWED);
        assertThat(handoverRule.networkCapabilities).containsExactly(
                NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_EIMS);
        assertThat(handoverRule.isOnlyForIncall).isTrue();

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("V2hhdCBUaGUgRnVjayBpcyB0aGlzIQ=="));
                () -> new HandoverRule("V2hhdCBUaGUgRnVjayBpcyB0aGlzIQ==", mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"));
                () -> new HandoverRule("target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed",
                                       mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"));
                () -> new HandoverRule("source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed",
                                        mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=GERAN, target=UNKNOWN, type=disallowed, "
                        + "capabilities=IMS"));
                        + "capabilities=IMS", mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=UNKNOWN, target=IWLAN, type=allowed, "
                        + "capabilities=IMS"));
                        + "capabilities=IMS", mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=GERAN, target=IWLAN, type=wtf"));
                () -> new HandoverRule("source=GERAN, target=IWLAN, type=wtf", mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=GERAN, target=NGRAN, type=allowed"));
                () -> new HandoverRule("source=GERAN, target=NGRAN, type=allowed", mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=IWLAN, target=WTFRAN, type=allowed"));
                () -> new HandoverRule("source=IWLAN, target=WTFRAN, type=allowed",
                                       mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=IWLAN, target=|, type=allowed"));
                () -> new HandoverRule("source=IWLAN, target=|, type=allowed", mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=GERAN, target=IWLAN, type=allowed, capabilities=|"));
                () -> new HandoverRule("source=GERAN, target=IWLAN, type=allowed, capabilities=|",
                                        mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=GERAN, target=IWLAN, type=allowed, capabilities="));
                () -> new HandoverRule("source=GERAN, target=IWLAN, type=allowed, capabilities=",
                                        mFeatureFlags));

        assertThrows(IllegalArgumentException.class,
                () -> new HandoverRule("source=GERAN, target=IWLAN, type=allowed, "
                        + "capabilities=wtf"));
                        + "capabilities=wtf", mFeatureFlags));
    }

    @Test
@@ -2919,6 +2938,35 @@ public class DataNetworkControllerTest extends TelephonyTest {
                .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
    }

    @Test
    public void testHandoverDataNetworkNotAllowedByIncallPolicy() throws Exception {
        mCarrierConfig.putStringArray(CarrierConfigManager.KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY,
                new String[]{"source=EUTRAN, target=IWLAN, type=disallowed, incall=true, "
                        + "capabilities=IMS"});
        carrierConfigChanged();
        testSetupImsDataNetwork();
        doReturn(PhoneConstants.State.RINGING).when(mCT).getState();

        // After this, IMS data network should be disconnected, and DNC should attempt to
        // establish a new one on IWLAN
        updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
                AccessNetworkConstants.TRANSPORT_TYPE_WLAN);

        // Verify all data disconnected.
        verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
        verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
                eq(DataCallResponse.LINK_STATUS_INACTIVE));

        // A new data network should be connected on IWLAN
        List<DataNetwork> dataNetworkList = getDataNetworks();
        assertThat(dataNetworkList).hasSize(1);
        assertThat(dataNetworkList.get(0).isConnected()).isTrue();
        assertThat(dataNetworkList.get(0).getNetworkCapabilities().hasCapability(
                NetworkCapabilities.NET_CAPABILITY_IMS)).isTrue();
        assertThat(dataNetworkList.get(0).getTransport())
                .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
    }

    @Test
    public void testHandoverDataNetworkRetry() throws Exception {
        testSetupImsDataNetwork();