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

Commit 214d26fb authored by Gwen Lin's avatar Gwen Lin Committed by Android (Google) Code Review
Browse files

Merge "Support handover rule based on incall" into main

parents 2d9ebf3e 30a4cfec
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
@@ -1280,7 +1280,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) + "]";
        }
    }

@@ -2353,6 +2365,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();
@@ -2360,6 +2375,7 @@ public class DataNetworkController extends Handler {
                    + "source=" + AccessNetworkType.toString(sourceAccessNetwork)
                    + ", target=" + AccessNetworkType.toString(targetAccessNetwork)
                    + ", roaming=" + isRoaming
                    + ", incall=" + isIncall
                    + ", ServiceState=" + mServiceState
                    + ", capabilities=" + capabilities);

@@ -2371,6 +2387,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());

@@ -2563,7 +2564,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);
@@ -2575,7 +2576,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);
@@ -2585,7 +2586,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);
@@ -2594,7 +2595,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,
@@ -2603,44 +2605,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
@@ -3107,6 +3126,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();