Set max DTIM multiplier in IpClient by using new AIDL method.
Set maximum acceptable DTIM multiplier to hardware driver per network
conditions(e.g. IPv4-only or IPv6-only or dual-stack networks) or when
multicast lock is held. Add integration test casese to verify the
multiplier value to be set in possible cases, and make the tests run
for both signature and root tests.
Bug: 266256943
Test: atest NetworkStackIntegrationTests
Test: atest NetworkStackRootTests
Test: manual test
- IPv4 only network, see device sets multiplier to 1 first and
set it back to 9 after 18s delay;
- Dual-stack network, IPv4 address shows up first and then device
sets multiplier to 1 temporarily, set it back to 2 when IPv6
address shows up (does nothing after 18s delay);
- IPv6 only network, see device sets multiplier to 2, does nothing
after 18s delay.
Change-Id: Ifd9223da9a63a71b38c8b4ecfa8c6f34b8891def
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 5cd56ae..9abaf92 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -21,6 +21,7 @@
import static android.net.ip.IIpClient.PROV_IPV4_DISABLED;
import static android.net.ip.IIpClient.PROV_IPV6_DISABLED;
import static android.net.ip.IIpClient.PROV_IPV6_LINKLOCAL;
+import static android.net.ip.IIpClientCallbacks.DTIM_MULTIPLIER_RESET;
import static android.net.ip.IpReachabilityMonitor.INVALID_REACHABILITY_LOSS_TYPE;
import static android.net.ip.IpReachabilityMonitor.nudEventTypeToInt;
import static android.net.util.SocketUtils.makePacketSocketAddress;
@@ -104,6 +105,7 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.SharedLog;
@@ -483,6 +485,7 @@
private static final int CMD_UPDATE_L2KEY_CLUSTER = 15;
private static final int CMD_COMPLETE_PRECONNECTION = 16;
private static final int CMD_UPDATE_L2INFORMATION = 17;
+ private static final int CMD_SET_DTIM_MULTIPLIER_AFTER_DELAY = 18;
private static final int ARG_LINKPROP_CHANGED_LINKSTATE_DOWN = 0;
private static final int ARG_LINKPROP_CHANGED_LINKSTATE_UP = 1;
@@ -506,6 +509,47 @@
private static final int DEFAULT_MIN_RDNSS_LIFETIME =
ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q) ? 120 : 0;
+ // Used to wait for the provisioning to complete eventually and then decide the target
+ // network type, which gives the accurate hint to set DTIM multiplier. Per current IPv6
+ // provisioning connection latency metrics, the latency of 95% can go up to 16s, so pick
+ // ProvisioningConfiguration.DEFAULT_TIMEOUT_MS value for this delay.
+ @VisibleForTesting
+ static final String CONFIG_INITIAL_PROVISIONING_DTIM_DELAY_MS =
+ "ipclient_initial_provisioning_dtim_delay";
+ private static final int DEFAULT_INITIAL_PROVISIONING_DTIM_DELAY_MS = 18000;
+
+ @VisibleForTesting
+ static final String CONFIG_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER =
+ "ipclient_multicast_lock_max_dtim_multiplier";
+ @VisibleForTesting
+ static final int DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER = 1;
+
+ @VisibleForTesting
+ static final String CONFIG_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER =
+ "ipclient_ipv6_only_max_dtim_multiplier";
+ @VisibleForTesting
+ static final int DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = 2;
+
+ @VisibleForTesting
+ static final String CONFIG_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER =
+ "ipclient_ipv4_only_max_dtim_multiplier";
+ @VisibleForTesting
+ static final int DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = 9;
+
+ @VisibleForTesting
+ static final String CONFIG_DUAL_STACK_MAX_DTIM_MULTIPLIER =
+ "ipclient_dual_stack_max_dtim_multiplier";
+ // The default value for dual-stack networks is the min of maximum DTIM multiplier to use for
+ // IPv4-only and IPv6-only networks.
+ @VisibleForTesting
+ static final int DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER = 2;
+
+ @VisibleForTesting
+ static final String CONFIG_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER =
+ "ipclient_before_ipv6_prov_max_dtim_multiplier";
+ @VisibleForTesting
+ static final int DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER = 1;
+
private static final boolean NO_CALLBACKS = false;
private static final boolean SEND_CALLBACKS = true;
@@ -595,9 +639,11 @@
private String mCluster; // The cluster for this network, for writing into the memory store
private boolean mMulticastFiltering;
private long mStartTimeMillis;
+ private long mInitialProvisioningEndTimeMillis;
private MacAddress mCurrentBssid;
private boolean mHasDisabledIpv6OrAcceptRaOnProvLoss;
private Integer mDadTransmits = null;
+ private int mMaxDtimMultiplier = DTIM_MULTIPLIER_RESET;
/**
* Reading the snapshot is an asynchronous operation initiated by invoking
@@ -1817,6 +1863,10 @@
maybeSaveNetworkToIpMemoryStore();
if (sendCallbacks) {
dispatchCallback(delta, newLp);
+ // We cannot do this along with onProvisioningSuccess callback, because the network
+ // can become dual-stack after a success IPv6 provisioning, and the multiplier also
+ // needs to be updated upon the loss of IPv4 and/or IPv6 provisioning.
+ updateMaxDtimMultiplier();
}
return (delta != PROV_CHANGE_LOST_PROVISIONING);
}
@@ -2208,6 +2258,12 @@
maybeRestoreInterfaceMtu();
// Reset number of dad_transmits to default value if changed.
maybeRestoreDadTransmits();
+ // Reset DTIM multiplier to default value if changed.
+ if (mMaxDtimMultiplier != DTIM_MULTIPLIER_RESET) {
+ mCallback.setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET);
+ mMaxDtimMultiplier = DTIM_MULTIPLIER_RESET;
+ mInitialProvisioningEndTimeMillis = 0;
+ }
}
@Override
@@ -2386,11 +2442,19 @@
public void enter() {
mIpProvisioningMetrics.reset();
mStartTimeMillis = SystemClock.elapsedRealtime();
+ final int delay = mDependencies.getDeviceConfigPropertyInt(
+ CONFIG_INITIAL_PROVISIONING_DTIM_DELAY_MS,
+ DEFAULT_INITIAL_PROVISIONING_DTIM_DELAY_MS);
+ mInitialProvisioningEndTimeMillis = mStartTimeMillis + delay;
+
if (mConfiguration.mProvisioningTimeoutMs > 0) {
final long alarmTime = SystemClock.elapsedRealtime()
+ mConfiguration.mProvisioningTimeoutMs;
mProvisioningTimeoutAlarm.schedule(alarmTime);
}
+ // Send a delay message to wait for IP provisioning to complete eventually and set the
+ // specific DTIM multiplier by checking the target network type.
+ sendMessageDelayed(CMD_SET_DTIM_MULTIPLIER_AFTER_DELAY, delay);
}
@Override
@@ -2627,6 +2691,7 @@
} else {
mCallback.setFallbackMulticastFilter(mMulticastFiltering);
}
+ updateMaxDtimMultiplier();
break;
}
@@ -2745,6 +2810,10 @@
mDhcpClient = null;
break;
+ case CMD_SET_DTIM_MULTIPLIER_AFTER_DELAY:
+ updateMaxDtimMultiplier();
+ break;
+
default:
return NOT_HANDLED;
}
@@ -2754,6 +2823,71 @@
}
}
+ /**
+ * Set the maximum DTIM multiplier to hardware driver per network condition. Any multiplier
+ * larger than the maximum value must not be accepted, it will cause packet loss higher than
+ * what the system can accept, which will cause unexpected behavior for apps, and may interrupt
+ * the network connection.
+ *
+ * When Wifi STA is in the power saving mode and the system is suspended, the wakeup interval
+ * will be set to:
+ * 1) multiplier * AP's DTIM period if multiplier > 0.
+ * 2) the driver default value if multiplier <= 0.
+ * Some implementations may apply an additional cap to wakeup interval in the case of 1).
+ */
+ private void updateMaxDtimMultiplier() {
+ int multiplier = deriveDtimMultiplier();
+ if (mMaxDtimMultiplier == multiplier) return;
+
+ mMaxDtimMultiplier = multiplier;
+ log("set max DTIM multiplier to " + multiplier);
+ mCallback.setMaxDtimMultiplier(multiplier);
+ }
+
+ private int deriveDtimMultiplier() {
+ final boolean hasIpv4Addr = mLinkProperties.hasIpv4Address();
+ // For a host in the network that has only ULA and link-local but no GUA, consider
+ // that it also has IPv6 connectivity. LinkProperties#isIpv6Provisioned only returns
+ // true when it has a GUA, so we cannot use it for IPv6-only network case.
+ final boolean hasIpv6Addr = CollectionUtils.any(mLinkProperties.getLinkAddresses(),
+ la -> {
+ final InetAddress address = la.getAddress();
+ return (address instanceof Inet6Address) && !address.isLinkLocalAddress();
+ });
+
+ final int multiplier;
+ if (!mMulticastFiltering) {
+ multiplier = mDependencies.getDeviceConfigPropertyInt(
+ CONFIG_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER,
+ DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER);
+ } else if (!hasIpv6Addr
+ && (SystemClock.elapsedRealtime() < mInitialProvisioningEndTimeMillis)) {
+ // IPv6 provisioning may or may not complete soon in the future, we don't know when
+ // it will complete, however, setting multiplier to a high value will cause higher
+ // RA packet loss, that increases the overall IPv6 provisioning latency. So just set
+ // multiplier to 1 before device gains the IPv6 provisioning, make sure device won't
+ // miss any RA packet later.
+ multiplier = mDependencies.getDeviceConfigPropertyInt(
+ CONFIG_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER,
+ DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
+ } else if (hasIpv6Addr && !hasIpv4Addr) {
+ multiplier = mDependencies.getDeviceConfigPropertyInt(
+ CONFIG_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER,
+ DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
+ } else if (hasIpv4Addr && !hasIpv6Addr) {
+ multiplier = mDependencies.getDeviceConfigPropertyInt(
+ CONFIG_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER,
+ DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
+ } else if (hasIpv6Addr && hasIpv4Addr) {
+ multiplier = mDependencies.getDeviceConfigPropertyInt(
+ CONFIG_DUAL_STACK_MAX_DTIM_MULTIPLIER,
+ DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER);
+ } else {
+ multiplier = DTIM_MULTIPLIER_RESET;
+ }
+ return multiplier;
+ }
+
private static class MessageHandlingLogger {
public String processedInState;
public String receivedInState;
diff --git a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
index c9c0e01..f4618a6 100644
--- a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
+++ b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
@@ -35,6 +35,7 @@
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS;
import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable;
+import static android.net.ip.IIpClientCallbacks.DTIM_MULTIPLIER_RESET;
import static android.net.ip.IpClientLinkObserver.CLAT_PREFIX;
import static android.net.ip.IpClientLinkObserver.CONFIG_SOCKET_RECV_BUFSIZE;
import static android.net.ip.IpReachabilityMonitor.NUD_MCAST_RESOLICIT_NUM;
@@ -86,6 +87,7 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.longThat;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.atLeastOnce;
@@ -566,6 +568,8 @@
protected abstract void setFeatureEnabled(String name, boolean enabled);
+ protected abstract void setDeviceConfigProperty(String name, int value);
+
protected abstract boolean isFeatureEnabled(String name, boolean defaultEnabled);
protected abstract boolean useNetworkStackSignature();
@@ -598,6 +602,21 @@
isIPv6OnlyPreferredEnabled);
}
+ private void setDeviceConfigForMaxDtimMultiplier() {
+ setDeviceConfigProperty(IpClient.CONFIG_INITIAL_PROVISIONING_DTIM_DELAY_MS,
+ 500 /* default value */);
+ setDeviceConfigProperty(IpClient.CONFIG_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER,
+ IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER);
+ setDeviceConfigProperty(IpClient.CONFIG_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER,
+ IpClient.DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
+ setDeviceConfigProperty(IpClient.CONFIG_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER,
+ IpClient.DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
+ setDeviceConfigProperty(IpClient.CONFIG_DUAL_STACK_MAX_DTIM_MULTIPLIER,
+ IpClient.DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER);
+ setDeviceConfigProperty(IpClient.CONFIG_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER,
+ IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
+ }
+
@Before
public void setUp() throws Exception {
// Suffix "[0]" or "[1]" is added to the end of test method name after running with
@@ -644,6 +663,7 @@
// Enable multicast filtering after creating IpClient instance, make the integration test
// more realistic.
mIIpClient.setMulticastFilter(true);
+ setDeviceConfigForMaxDtimMultiplier();
}
protected void setUpMocks() throws Exception {
@@ -664,6 +684,12 @@
when(mNetworkStackServiceManager.getIpMemoryStoreService())
.thenReturn(mIpMemoryStoreService);
when(mCb.getInterfaceVersion()).thenReturn(IpClient.VERSION_ADDED_REACHABILITY_FAILURE);
+ // This mock is required, otherwise, ignoreIPv6ProvisioningLoss variable is always true,
+ // and IpReachabilityMonitor#avoidingBadLinks() will always return false as well, that
+ // results in the target tested IPv6 off-link DNS server won't be removed from LP and
+ // notifyLost won't be invoked, or the wrong code path when receiving RA with 0 router
+ // liftime.
+ when(mCm.shouldAvoidBadWifi()).thenReturn(true);
mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67);
mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10);
@@ -2856,11 +2882,14 @@
final InOrder inOrder = inOrder(mCb);
final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
- doIpv6OnlyProvisioning(inOrder, ra);
-
- // Start IPv4 provisioning and wait until entire provisioning completes.
+ // Start IPv4 provisioning first and then IPv6 provisioning, which is more realistic.
+ // And wait until entire provisioning completes.
handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
+
+ waitForRouterSolicitation();
+ mPacketReader.sendResponse(ra);
+
verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> {
if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false;
lpFuture.complete(x);
@@ -2871,13 +2900,14 @@
assertNotNull(lp);
assertTrue(lp.getDnsServers().contains(dnsServer));
assertTrue(lp.getDnsServers().contains(SERVER_ADDR));
+ assertHasAddressThat("link-local address", lp, x -> x.getAddress().isLinkLocalAddress());
+ assertHasAddressThat("privacy address", lp, this::isPrivacyAddress);
+ assertHasAddressThat("stable privacy address", lp, this::isStablePrivacyAddress);
return lp;
}
private void doDualStackProvisioning(boolean shouldDisableAcceptRa) throws Exception {
- when(mCm.shouldAvoidBadWifi()).thenReturn(true);
-
final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
.withoutIpReachabilityMonitor()
.build();
@@ -2888,7 +2918,12 @@
// not strictly necessary.
setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
- mIpc.startProvisioning(config);
+ // Both signature and root tests can use this function to do dual-stack provisioning.
+ if (useNetworkStackSignature()) {
+ mIpc.startProvisioning(config);
+ } else {
+ mIIpClient.startProvisioning(config.toStableParcelable());
+ }
performDualStackProvisioning();
}
@@ -3940,11 +3975,6 @@
final boolean isIgnoreIncompleteIpv6DnsServerEnabled,
final boolean isIgnoreIncompleteIpv6DefaultRouterEnabled,
final boolean expectNeighborLost) throws Exception {
- // This mock is required, otherwise, IpReachabilityMonitor#avoidingBadLinks() will always
- // return false, that results in the target tested IPv6 off-link DNS server won't be removed
- // from LP and notifyLost won't be invoked.
- when(mCm.shouldAvoidBadWifi()).thenReturn(true);
-
mNetworkAgentThread =
new HandlerThread(IpClientIntegrationTestCommon.class.getSimpleName());
mNetworkAgentThread.start();
@@ -4331,4 +4361,106 @@
assertFalse(lp.hasGlobalIpv6Address());
assertEquals(1, lp.getLinkAddresses().size()); // only link-local
}
+
+ @Test
+ public void testMaxDtimMultiplier_IPv6OnlyNetwork() throws Exception {
+ ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+ .withoutIPv4()
+ .build();
+ startIpClientProvisioning(config);
+
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
+ IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
+
+ LinkProperties lp = doIpv6OnlyProvisioning();
+ assertNotNull(lp);
+ assertEquals(3, lp.getLinkAddresses().size()); // IPv6 privacy, stable privacy, link-local
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
+ IpClient.DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
+ }
+
+ @Test
+ public void testMaxDtimMultiplier_IPv6LinkLocalOnlyMode() throws Exception {
+ final InOrder inOrder = inOrder(mCb);
+ ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+ .withoutIPv4()
+ .withIpv6LinkLocalOnly()
+ .build();
+ startIpClientProvisioning(config);
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any());
+ inOrder.verify(mCb).setMaxDtimMultiplier(
+ IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
+ inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
+ DTIM_MULTIPLIER_RESET);
+ }
+
+ @Test
+ public void testMaxDtimMultiplier_IPv4OnlyNetwork() throws Exception {
+ final InOrder inOrder = inOrder(mCb);
+ performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
+ true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
+ TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
+ verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
+ inOrder.verify(mCb).setMaxDtimMultiplier(
+ IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
+ inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
+ IpClient.DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
+ }
+
+ private void runDualStackNetworkDtimMultiplierSetting(final InOrder inOrder) throws Exception {
+ doDualStackProvisioning(false /* shouldDisableAcceptRa */);
+ inOrder.verify(mCb).setMaxDtimMultiplier(
+ IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
+ inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
+ IpClient.DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER);
+ }
+
+ @Test
+ public void testMaxDtimMultiplier_DualStackNetwork() throws Exception {
+ final InOrder inOrder = inOrder(mCb);
+ runDualStackNetworkDtimMultiplierSetting(inOrder);
+ }
+
+ @Test
+ public void testMaxDtimMultiplier_MulticastLock() throws Exception {
+ final InOrder inOrder = inOrder(mCb);
+ runDualStackNetworkDtimMultiplierSetting(inOrder);
+
+ // Simulate to hold the multicast lock by disabling the multicast filter.
+ mIIpClient.setMulticastFilter(false);
+ inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
+ IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER);
+
+ // Simulate to disable the multicast lock again, then check the multiplier should be
+ // changed to 2 (dual-stack setting)
+ mIIpClient.setMulticastFilter(true);
+ inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
+ IpClient.DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER);
+ }
+
+ @Test
+ public void testMaxDtimMultiplier_MulticastLockEnabled_StoppedState() throws Exception {
+ // Simulate to hold the multicast lock by disabling the multicast filter at StoppedState,
+ // verify no callback to be sent, start dual-stack provisioning and verify the multiplier
+ // to be set to 1 (multicast lock setting) later.
+ mIIpClient.setMulticastFilter(false);
+ verify(mCb, after(10).never()).setMaxDtimMultiplier(
+ IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER);
+
+ doDualStackProvisioning(false /* shouldDisableAcceptRa */);
+ verify(mCb, times(1)).setMaxDtimMultiplier(
+ IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER);
+ }
+
+ @Test
+ public void testMaxDtimMultiplier_resetMultiplier() throws Exception {
+ final InOrder inOrder = inOrder(mCb);
+ runDualStackNetworkDtimMultiplierSetting(inOrder);
+
+ verify(mCb, never()).setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET);
+
+ // Stop IpClient and verify if the multiplier has been reset.
+ mIIpClient.stop();
+ inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET);
+ }
}
diff --git a/tests/integration/root/android/net/ip/IpClientRootTest.kt b/tests/integration/root/android/net/ip/IpClientRootTest.kt
index f1fa1c7..0f7ec0c 100644
--- a/tests/integration/root/android/net/ip/IpClientRootTest.kt
+++ b/tests/integration/root/android/net/ip/IpClientRootTest.kt
@@ -138,7 +138,7 @@
}
}
- private val originalFlagValues = ArrayMap<String, String>()
+ private val originalPropertyValues = ArrayMap<String, String>()
/**
* Wrapper class for IIpClientCallbacks.
@@ -154,11 +154,11 @@
}
@After
- fun tearDownFlags() {
+ fun tearDownDeviceConfigProperties() {
if (testSkipped()) return
automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG)
try {
- for ((key, value) in originalFlagValues.entries) {
+ for ((key, value) in originalPropertyValues.entries) {
if (key == null) continue
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, key,
value, false /* makeDefault */)
@@ -195,25 +195,34 @@
return ipClientCaptor.value
}
- override fun setFeatureEnabled(feature: String, enabled: Boolean) {
+ private fun setDeviceConfigProperty(name: String, value: String) {
automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG)
try {
- // Do not use computeIfAbsent as it would overwrite null values (flag originally unset)
- if (!originalFlagValues.containsKey(feature)) {
- originalFlagValues[feature] =
- DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature)
+ // Do not use computeIfAbsent as it would overwrite null values,
+ // property originally unset.
+ if (!originalPropertyValues.containsKey(name)) {
+ originalPropertyValues[name] =
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, name)
}
- // The feature is enabled if the flag is lower than the package version.
- // Package versions follow a standard format with 9 digits.
- // TODO: consider resetting flag values on reboot when set to special values like "1" or
- // "999999999"
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature,
- if (enabled) "1" else "999999999", false)
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, name, value,
+ false /* makeDefault */)
} finally {
automation.dropShellPermissionIdentity()
}
}
+ override fun setFeatureEnabled(feature: String, enabled: Boolean) {
+ // The feature is enabled if the flag is lower than the package version.
+ // Package versions follow a standard format with 9 digits.
+ // TODO: consider resetting flag values on reboot when set to special values like "1" or
+ // "999999999"
+ setDeviceConfigProperty(feature, if (enabled) "1" else "999999999")
+ }
+
+ override fun setDeviceConfigProperty(name: String, value: Int) {
+ setDeviceConfigProperty(name, value.toString())
+ }
+
override fun isFeatureEnabled(name: String, defaultEnabled: Boolean): Boolean {
automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG)
try {
diff --git a/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt b/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt
index 26938b6..b494732 100644
--- a/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt
+++ b/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt
@@ -52,6 +52,10 @@
mEnabledFeatures.put(name, enabled)
}
+ override fun setDeviceConfigProperty(name: String, value: Int) {
+ mDependencies.setDeviceConfigProperty(name, value)
+ }
+
override fun getStoredNetworkAttributes(l2Key: String, timeout: Long): NetworkAttributes {
val networkAttributesCaptor = ArgumentCaptor.forClass(NetworkAttributes::class.java)