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
diff --git a/flags/data.aconfig b/flags/data.aconfig
index 17d1adb..f3409f8 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -129,3 +129,14 @@
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
+ }
+}
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index 89b0fec..fc029af 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -1269,7 +1269,7 @@
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());
}
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index d5bc741..ddf8fff 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -741,6 +741,8 @@
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 @@
/** {@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 @@
*
* @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 @@
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 @@
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 @@
this.type = type;
networkCapabilities = capabilities;
isOnlyForRoaming = roaming;
+ isOnlyForIncall = incall;
}
@Override
@@ -876,8 +888,8 @@
.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 @@
// 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 @@
+ "source=" + AccessNetworkType.toString(sourceAccessNetwork)
+ ", target=" + AccessNetworkType.toString(targetAccessNetwork)
+ ", roaming=" + isRoaming
+ + ", incall=" + isIncall
+ ", ServiceState=" + mServiceState
+ ", capabilities=" + capabilities);
@@ -2355,6 +2371,11 @@
// 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)) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index 3f2d6f2..1c5d6f3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -890,6 +890,7 @@
.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 @@
@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 @@
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 @@
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 @@
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 @@
assertThat(handoverRule.networkCapabilities).containsExactly(
NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_EIMS);
- assertThrows(IllegalArgumentException.class,
- () -> new HandoverRule("V2hhdCBUaGUgRnVjayBpcyB0aGlzIQ=="));
+ 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("target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"));
+ () -> new HandoverRule("V2hhdCBUaGUgRnVjayBpcyB0aGlzIQ==", mFeatureFlags));
assertThrows(IllegalArgumentException.class,
- () -> new HandoverRule("source=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",
+ 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
@@ -2920,6 +2939,35 @@
}
@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();