1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.ip;
18 
19 import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
20 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
22 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
23 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
24 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
25 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
26 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
27 import static android.net.RouteInfo.RTN_UNICAST;
28 import static android.net.dhcp.DhcpClient.EXPIRED_LEASE;
29 import static android.net.dhcp.DhcpPacket.CONFIG_MINIMUM_LEASE;
30 import static android.net.dhcp.DhcpPacket.DHCP_BOOTREQUEST;
31 import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
32 import static android.net.dhcp.DhcpPacket.DHCP_IPV6_ONLY_PREFERRED;
33 import static android.net.dhcp.DhcpPacket.DHCP_MAGIC_COOKIE;
34 import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
35 import static android.net.dhcp.DhcpPacket.ENCAP_L2;
36 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
37 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
38 import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS;
39 import static android.net.ip.IIpClientCallbacks.DTIM_MULTIPLIER_RESET;
40 import static android.net.ip.IpClient.CONFIG_IPV6_AUTOCONF_TIMEOUT;
41 import static android.net.ip.IpClient.CONFIG_ACCEPT_RA_MIN_LFT;
42 import static android.net.ip.IpClient.CONFIG_APF_COUNTER_POLLING_INTERVAL_SECS;
43 import static android.net.ip.IpClient.CONFIG_NUD_FAILURE_COUNT_DAILY_THRESHOLD;
44 import static android.net.ip.IpClient.CONFIG_NUD_FAILURE_COUNT_WEEKLY_THRESHOLD;
45 import static android.net.ip.IpClient.DEFAULT_APF_COUNTER_POLLING_INTERVAL_SECS;
46 import static android.net.ip.IpClient.DEFAULT_NUD_FAILURE_COUNT_DAILY_THRESHOLD;
47 import static android.net.ip.IpClient.DEFAULT_NUD_FAILURE_COUNT_WEEKLY_THRESHOLD;
48 import static android.net.ip.IpClient.ONE_DAY_IN_MS;
49 import static android.net.ip.IpClient.ONE_WEEK_IN_MS;
50 import static android.net.ip.IpClient.SIX_HOURS_IN_MS;
51 import static android.net.ip.IpClientLinkObserver.CLAT_PREFIX;
52 import static android.net.ip.IpClientLinkObserver.CONFIG_SOCKET_RECV_BUFSIZE;
53 import static android.net.ip.IpReachabilityMonitor.NUD_MCAST_RESOLICIT_NUM;
54 import static android.net.ip.IpReachabilityMonitor.nudEventTypeToInt;
55 import static android.net.ipmemorystore.Status.SUCCESS;
56 import static android.system.OsConstants.ETH_P_IPV6;
57 import static android.system.OsConstants.IFA_F_TEMPORARY;
58 import static android.system.OsConstants.IPPROTO_ICMPV6;
59 import static android.system.OsConstants.IPPROTO_IPV6;
60 import static android.system.OsConstants.IPPROTO_UDP;
61 
62 import static com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress;
63 import static com.android.net.module.util.Inet4AddressUtils.getPrefixMaskAsInet4Address;
64 import static com.android.net.module.util.NetworkStackConstants.ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
65 import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY;
66 import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST;
67 import static com.android.net.module.util.NetworkStackConstants.DHCP6_CLIENT_PORT;
68 import static com.android.net.module.util.NetworkStackConstants.DHCP6_SERVER_PORT;
69 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
70 import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST;
71 import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN;
72 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_OFFSET;
73 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
74 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
75 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION;
76 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
77 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
78 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
79 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST;
80 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ANY;
81 import static com.android.net.module.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
82 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
83 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER;
84 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
85 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
86 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
87 import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION;
88 import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION;
89 import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION;
90 import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION;
91 import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION;
92 import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION;
93 import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION;
94 import static com.android.testutils.MiscAsserts.assertThrows;
95 import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
96 import static com.android.testutils.TestPermissionUtil.runAsShell;
97 
98 import static junit.framework.Assert.fail;
99 
100 import static org.junit.Assert.assertArrayEquals;
101 import static org.junit.Assert.assertEquals;
102 import static org.junit.Assert.assertFalse;
103 import static org.junit.Assert.assertNotEquals;
104 import static org.junit.Assert.assertNotNull;
105 import static org.junit.Assert.assertNull;
106 import static org.junit.Assert.assertTrue;
107 import static org.junit.Assume.assumeFalse;
108 import static org.junit.Assume.assumeTrue;
109 import static org.mockito.ArgumentMatchers.anyInt;
110 import static org.mockito.ArgumentMatchers.anyLong;
111 import static org.mockito.ArgumentMatchers.contains;
112 import static org.mockito.ArgumentMatchers.longThat;
113 import static org.mockito.Mockito.after;
114 import static org.mockito.Mockito.any;
115 import static org.mockito.Mockito.argThat;
116 import static org.mockito.Mockito.atLeastOnce;
117 import static org.mockito.Mockito.clearInvocations;
118 import static org.mockito.Mockito.doAnswer;
119 import static org.mockito.Mockito.doThrow;
120 import static org.mockito.Mockito.eq;
121 import static org.mockito.Mockito.inOrder;
122 import static org.mockito.Mockito.mock;
123 import static org.mockito.Mockito.never;
124 import static org.mockito.Mockito.reset;
125 import static org.mockito.Mockito.spy;
126 import static org.mockito.Mockito.timeout;
127 import static org.mockito.Mockito.times;
128 import static org.mockito.Mockito.verify;
129 import static org.mockito.Mockito.verifyNoMoreInteractions;
130 import static org.mockito.Mockito.when;
131 
132 import android.app.AlarmManager;
133 import android.app.AlarmManager.OnAlarmListener;
134 import android.app.Instrumentation;
135 import android.app.admin.DevicePolicyManager;
136 import android.content.ComponentName;
137 import android.content.ContentResolver;
138 import android.content.Context;
139 import android.content.pm.PackageManager;
140 import android.content.res.Resources;
141 import android.net.ConnectivityManager;
142 import android.net.DhcpResultsParcelable;
143 import android.net.IIpMemoryStore;
144 import android.net.INetd;
145 import android.net.InetAddresses;
146 import android.net.InterfaceConfigurationParcel;
147 import android.net.IpPrefix;
148 import android.net.Layer2InformationParcelable;
149 import android.net.Layer2PacketParcelable;
150 import android.net.LinkAddress;
151 import android.net.LinkProperties;
152 import android.net.MacAddress;
153 import android.net.Network;
154 import android.net.NetworkAgentConfig;
155 import android.net.NetworkCapabilities;
156 import android.net.NetworkRequest;
157 import android.net.NetworkSpecifier;
158 import android.net.NetworkStackIpMemoryStore;
159 import android.net.RouteInfo;
160 import android.net.TestNetworkInterface;
161 import android.net.TestNetworkManager;
162 import android.net.Uri;
163 import android.net.dhcp.DhcpClient;
164 import android.net.dhcp.DhcpDeclinePacket;
165 import android.net.dhcp.DhcpDiscoverPacket;
166 import android.net.dhcp.DhcpPacket;
167 import android.net.dhcp.DhcpPacket.ParseException;
168 import android.net.dhcp.DhcpRequestPacket;
169 import android.net.dhcp6.Dhcp6Client;
170 import android.net.dhcp6.Dhcp6Packet;
171 import android.net.dhcp6.Dhcp6Packet.PrefixDelegation;
172 import android.net.dhcp6.Dhcp6RebindPacket;
173 import android.net.dhcp6.Dhcp6RenewPacket;
174 import android.net.dhcp6.Dhcp6RequestPacket;
175 import android.net.dhcp6.Dhcp6SolicitPacket;
176 import android.net.ipmemorystore.NetworkAttributes;
177 import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
178 import android.net.ipmemorystore.OnNetworkEventCountRetrievedListener;
179 import android.net.ipmemorystore.Status;
180 import android.net.networkstack.TestNetworkStackServiceClient;
181 import android.net.networkstack.aidl.dhcp.DhcpOption;
182 import android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable;
183 import android.net.networkstack.aidl.ip.ReachabilityLossReason;
184 import android.net.shared.Layer2Information;
185 import android.net.shared.ProvisioningConfiguration;
186 import android.net.shared.ProvisioningConfiguration.ScanResultInfo;
187 import android.net.util.HostnameTransliterator;
188 import android.os.Build;
189 import android.os.Handler;
190 import android.os.HandlerThread;
191 import android.os.IBinder;
192 import android.os.ParcelFileDescriptor;
193 import android.os.PowerManager;
194 import android.os.RemoteException;
195 import android.os.SystemClock;
196 import android.provider.Settings;
197 import android.stats.connectivity.NudEventType;
198 import android.system.ErrnoException;
199 import android.system.Os;
200 import android.util.Log;
201 import android.util.Pair;
202 
203 import androidx.annotation.NonNull;
204 import androidx.test.InstrumentationRegistry;
205 import androidx.test.filters.SmallTest;
206 
207 import com.android.internal.util.HexDump;
208 import com.android.internal.util.StateMachine;
209 import com.android.modules.utils.build.SdkLevel;
210 import com.android.net.module.util.ArrayTrackRecord;
211 import com.android.net.module.util.InterfaceParams;
212 import com.android.net.module.util.Ipv6Utils;
213 import com.android.net.module.util.PacketBuilder;
214 import com.android.net.module.util.SharedLog;
215 import com.android.net.module.util.Struct;
216 import com.android.net.module.util.arp.ArpPacket;
217 import com.android.net.module.util.ip.IpNeighborMonitor;
218 import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEventConsumer;
219 import com.android.net.module.util.netlink.NetlinkUtils;
220 import com.android.net.module.util.netlink.StructNdOptPref64;
221 import com.android.net.module.util.structs.EthernetHeader;
222 import com.android.net.module.util.structs.IaPrefixOption;
223 import com.android.net.module.util.structs.Ipv6Header;
224 import com.android.net.module.util.structs.LlaOption;
225 import com.android.net.module.util.structs.PrefixInformationOption;
226 import com.android.net.module.util.structs.RdnssOption;
227 import com.android.networkstack.R;
228 import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
229 import com.android.networkstack.ipmemorystore.IpMemoryStoreService;
230 import com.android.networkstack.metrics.IpProvisioningMetrics;
231 import com.android.networkstack.metrics.IpReachabilityMonitorMetrics;
232 import com.android.networkstack.metrics.NetworkQuirkMetrics;
233 import com.android.networkstack.packets.NeighborAdvertisement;
234 import com.android.networkstack.packets.NeighborSolicitation;
235 import com.android.networkstack.util.NetworkStackUtils;
236 import com.android.server.NetworkStackService.NetworkStackServiceManager;
237 import com.android.testutils.CompatUtil;
238 import com.android.testutils.DevSdkIgnoreRule;
239 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
240 import com.android.testutils.HandlerUtils;
241 import com.android.testutils.PollPacketReader;
242 import com.android.testutils.TestableNetworkAgent;
243 import com.android.testutils.TestableNetworkCallback;
244 
245 import kotlin.Lazy;
246 import kotlin.LazyKt;
247 
248 import org.junit.After;
249 import org.junit.Before;
250 import org.junit.Rule;
251 import org.junit.Test;
252 import org.junit.rules.TestName;
253 import org.mockito.ArgumentCaptor;
254 import org.mockito.InOrder;
255 import org.mockito.Mock;
256 import org.mockito.MockitoAnnotations;
257 import org.mockito.Spy;
258 
259 import java.io.BufferedReader;
260 import java.io.File;
261 import java.io.FileDescriptor;
262 import java.io.FileReader;
263 import java.io.IOException;
264 import java.lang.annotation.ElementType;
265 import java.lang.annotation.Repeatable;
266 import java.lang.annotation.Retention;
267 import java.lang.annotation.RetentionPolicy;
268 import java.lang.annotation.Target;
269 import java.lang.reflect.Method;
270 import java.net.DatagramPacket;
271 import java.net.DatagramSocket;
272 import java.net.Inet4Address;
273 import java.net.Inet6Address;
274 import java.net.InetAddress;
275 import java.net.NetworkInterface;
276 import java.nio.ByteBuffer;
277 import java.util.ArrayList;
278 import java.util.Arrays;
279 import java.util.Collection;
280 import java.util.Collections;
281 import java.util.List;
282 import java.util.Objects;
283 import java.util.Random;
284 import java.util.concurrent.CompletableFuture;
285 import java.util.concurrent.CountDownLatch;
286 import java.util.concurrent.TimeUnit;
287 import java.util.concurrent.atomic.AtomicReference;
288 import java.util.function.Predicate;
289 
290 /**
291  * Base class for IpClient tests.
292  *
293  * Tests in this class can either be run with signature permissions, or with root access.
294  */
295 @SmallTest
296 public abstract class IpClientIntegrationTestCommon {
297     private static final String TAG = IpClientIntegrationTestCommon.class.getSimpleName();
298     private static final int DATA_BUFFER_LEN = 4096;
299     private static final int PACKET_TIMEOUT_MS = 5_000;
300     private static final String TEST_CLUSTER = "some cluster";
301     private static final int TEST_LEASE_DURATION_S = 3_600; // 1 hour
302     private static final int TEST_IPV6_ONLY_WAIT_S = 1_800; // 30 min
303     private static final int TEST_LOWER_IPV6_ONLY_WAIT_S = (int) (MIN_V6ONLY_WAIT_MS / 1000 - 1);
304     private static final int TEST_ZERO_IPV6_ONLY_WAIT_S = 0;
305     private static final long TEST_MAX_IPV6_ONLY_WAIT_S = 0xffffffffL;
306     private static final int TEST_DEVICE_OWNER_APP_UID = 14242;
307     private static final String TEST_DEVICE_OWNER_APP_PACKAGE = "com.example.deviceowner";
308     protected static final String TEST_L2KEY = "some l2key";
309 
310     // TODO: move to NetlinkConstants, NetworkStackConstants, or OsConstants.
311     private static final int IFA_F_STABLE_PRIVACY = 0x800;
312     // To fix below AndroidLint warning:
313     // [InlinedApi] Field requires version 3 of the U Extensions SDK (current min is 0).
314     private static final int RTN_UNREACHABLE =
315             SdkLevel.isAtLeastT() ? RouteInfo.RTN_UNREACHABLE : 7;
316 
317     protected static final long TEST_TIMEOUT_MS = 2_000L;
318     private static final long TEST_WAIT_ENOBUFS_TIMEOUT_MS = 30_000L;
319     private static final long TEST_WAIT_RENEW_REBIND_RETRANSMIT_MS = 15_000L;
320     // To prevent the flakiness about deprecationTime and expirationTime check, +/- 4s tolerance
321     // should be enough between the timestamp when the IP provisioning completes successfully and
322     // when IpClientLinkObserver sees the RTM_NEWADDR netlink events.
323     private static final long TEST_LIFETIME_TOLERANCE_MS = 4_000L;
324 
325     @Rule
326     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
327     @Rule
328     public final TestName mTestNameRule = new TestName();
329 
330     /**
331      * Indicates that a test requires signature permissions to run.
332      *
333      * Such tests can only be run on devices that use known signing keys, so this annotation must be
334      * avoided as much as possible. Consider whether the test can be written to use shell and root
335      * shell permissions, and run against the NetworkStack AIDL interface (IIpClient) instead.
336      */
337     @Retention(RetentionPolicy.RUNTIME)
338     @Target({ElementType.METHOD})
339     private @interface SignatureRequiredTest {
reason()340         String reason();
341     }
342 
343     @Retention(RetentionPolicy.RUNTIME)
344     @Target({ElementType.METHOD})
345     @Repeatable(FlagArray.class)
346     @interface Flag {
name()347         String name();
enabled()348         boolean enabled();
349     }
350 
351     @Target({ElementType.METHOD})
352     @Retention(RetentionPolicy.RUNTIME)
353     @interface FlagArray {
value()354         Flag[] value();
355     }
356 
357     /**** BEGIN signature required test members ****/
358     // Do not use unless the test *really* cannot be written to exercise IIpClient without mocks.
359     // Tests using the below members must be annotated with @SignatureRequiredTest (otherwise the
360     // members will be null), and can only be run on devices that use known signing keys.
361     // The members could technically be moved to the IpClientIntegrationTest subclass together with
362     // the tests requiring signature permissions, but this would make it harder to follow tests in
363     // multiple classes, and harder to migrate tests between signature required and not required.
364 
365     @Mock private Context mContext;
366     @Mock private ConnectivityManager mCm;
367     @Mock private Resources mResources;
368     @Mock private AlarmManager mAlarm;
369     @Mock private ContentResolver mContentResolver;
370     @Mock private NetworkStackServiceManager mNetworkStackServiceManager;
371     @Mock private IpMemoryStoreService mIpMemoryStoreService;
372     @Mock private PowerManager.WakeLock mTimeoutWakeLock;
373     @Mock protected NetworkStackIpMemoryStore mIpMemoryStore;
374     @Mock private NetworkQuirkMetrics.Dependencies mNetworkQuirkMetricsDeps;
375     @Mock private IpReachabilityMonitorMetrics mIpReachabilityMonitorMetrics;
376     @Mock private DevicePolicyManager mDevicePolicyManager;
377     @Mock private PackageManager mPackageManager;
378     @Spy private INetd mNetd;
379 
380     protected IpClient mIpc;
381     protected Dependencies mDependencies;
382     protected List<Pair<String, Pair<Long, Integer>>> mNetworkEvents = new ArrayList<>();
383 
384     /***** END signature required test members *****/
385 
386     protected IIpClientCallbacks mCb;
387     private IIpClient mIIpClient;
388     private String mIfaceName;
389     private HandlerThread mPacketReaderThread;
390     private Handler mHandler;
391     private PollPacketReader mPacketReader;
392     private FileDescriptor mTapFd;
393     private byte[] mClientMac;
394     private InetAddress mClientIpAddress;
395     private TestableNetworkAgent mNetworkAgent;
396     private HandlerThread mNetworkAgentThread;
397 
398     private boolean mIsSignatureRequiredTest;
399 
400     // ReadHeads for various packet streams. Cannot be initialized in @Before because ReadHead is
401     // single-thread-only, and AndroidJUnitRunner runs @Before and @Test on different threads.
402     // While it looks like these are created only once per test, they are actually created once per
403     // test method because JUnit recreates a fresh test class instance before every test method.
404     private Lazy<ArrayTrackRecord<byte[]>.ReadHead> mDhcpPacketReadHead =
405             LazyKt.lazy(() -> mPacketReader.getReceivedPackets().newReadHead());
406     private Lazy<ArrayTrackRecord<byte[]>.ReadHead> mArpPacketReadHead =
407             LazyKt.lazy(() -> mPacketReader.getReceivedPackets().newReadHead());
408     private Lazy<ArrayTrackRecord<byte[]>.ReadHead> mDhcp6PacketReadHead =
409             LazyKt.lazy(() -> mPacketReader.getReceivedPackets().newReadHead());
410 
411     // Ethernet header
412     private static final int ETH_HEADER_LEN = 14;
413 
414     // IP header
415     private static final int IPV4_HEADER_LEN = 20;
416     private static final int IPV6_HEADER_LEN = 40;
417     private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
418     private static final int IPV4_DST_ADDR_OFFSET = IPV4_SRC_ADDR_OFFSET + 4;
419 
420     // UDP header
421     private static final int UDP_HEADER_LEN = 8;
422     private static final int UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
423     private static final int UDP_SRC_PORT_OFFSET = UDP_HEADER_OFFSET + 0;
424 
425     // DHCP header
426     private static final int DHCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN
427             + UDP_HEADER_LEN;
428     private static final int DHCP_MESSAGE_OP_CODE_OFFSET = DHCP_HEADER_OFFSET + 0;
429     private static final int DHCP_TRANSACTION_ID_OFFSET = DHCP_HEADER_OFFSET + 4;
430     private static final int DHCP_OPTION_MAGIC_COOKIE_OFFSET = DHCP_HEADER_OFFSET + 236;
431 
432     // DHCPv6 header
433     private static final int DHCP6_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN
434             + UDP_HEADER_LEN;
435 
436     private static final Inet4Address SERVER_ADDR = ipv4Addr("192.168.1.100");
437     private static final Inet4Address CLIENT_ADDR = ipv4Addr("192.168.1.2");
438     private static final Inet4Address CLIENT_ADDR_NEW = ipv4Addr("192.168.1.3");
439     private static final Inet4Address INADDR_ANY = ipv4Addr("0.0.0.0");
440     private static final int PREFIX_LENGTH = 24;
441     private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
442     private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
443             SERVER_ADDR, PREFIX_LENGTH);
444     private static final String IPV6_LINK_LOCAL_PREFIX = "fe80::/64";
445     private static final String IPV4_TEST_SUBNET_PREFIX = "192.168.1.0/24";
446     private static final String IPV4_ANY_ADDRESS_PREFIX = "0.0.0.0/0";
447     private static final String HOSTNAME = "testhostname";
448     private static final String IPV6_OFF_LINK_DNS_SERVER = "2001:4860:4860::64";
449     private static final String IPV6_ON_LINK_DNS_SERVER = "2001:db8:1::64";
450     private static final int TEST_DEFAULT_MTU = 1500;
451     private static final int TEST_MIN_MTU = 1280;
452     private static final MacAddress ROUTER_MAC = MacAddress.fromString("00:1A:11:22:33:44");
453     private static final byte[] ROUTER_MAC_BYTES = ROUTER_MAC.toByteArray();
454     private static final Inet6Address ROUTER_LINK_LOCAL = ipv6Addr("fe80::1");
455     private static final byte[] ROUTER_DUID = new byte[] {
456             // type: Link-layer address, hardware type: EUI64(27)
457             (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x1b,
458             // set 7th bit, and copy the first 3 bytes of mac address
459             (byte) 0x02, (byte) 0x1A, (byte) 0x11,
460             (byte) 0xFF, (byte) 0xFE,
461             // copy the last 3 bytes of mac address
462             (byte) 0x22, (byte) 0x33, (byte) 0x44,
463     };
464     private static final String TEST_HOST_NAME = "AOSP on Crosshatch";
465     private static final String TEST_HOST_NAME_TRANSLITERATION = "AOSP-on-Crosshatch";
466     private static final String TEST_CAPTIVE_PORTAL_URL = "https://example.com/capportapi";
467     private static final byte[] TEST_HOTSPOT_OUI = new byte[] {
468             (byte) 0x00, (byte) 0x17, (byte) 0xF2
469     };
470     private static final byte LEGACY_TEST_VENDOR_SPECIFIC_IE_TYPE = 0x11;
471     private static final byte TEST_VENDOR_SPECIFIC_IE_TYPE = 0x21;
472     private static final int TEST_VENDOR_SPECIFIC_IE_ID = 0xdd;
473 
474     private static final String TEST_DEFAULT_SSID = "test_ssid";
475     private static final String TEST_DEFAULT_BSSID = "00:11:22:33:44:55";
476     private static final String TEST_DHCP_ROAM_SSID = "0001docomo";
477     private static final String TEST_DHCP_ROAM_BSSID = "00:4e:35:17:98:55";
478     private static final String TEST_DHCP_ROAM_L2KEY = "roaming_l2key";
479     private static final String TEST_DHCP_ROAM_CLUSTER = "roaming_cluster";
480     private static final byte[] TEST_AP_OUI = new byte[] { 0x00, 0x1A, 0x11 };
481     private static final byte[] TEST_OEM_OUI = new byte[] {(byte) 0x00, (byte) 0x17, (byte) 0xc3};
482     private static final String TEST_OEM_VENDOR_ID = "vendor-class-identifier";
483     private static final byte[] TEST_OEM_USER_CLASS_INFO = new byte[] {
484             // Instance of User Class: [0]
485             (byte) 0x03, /* UC_Len_0 */ (byte) 0x11, (byte) 0x22, (byte) 0x33,
486             // Instance of User Class: [1]
487             (byte) 0x03, /* UC_Len_1 */ (byte) 0x44, (byte) 0x55, (byte) 0x66,
488     };
489 
490     protected class Dependencies extends IpClient.Dependencies {
491         private DhcpClient mDhcpClient;
492         private Dhcp6Client mDhcp6Client;
493         private boolean mIsHostnameConfigurationEnabled;
494         private String mHostname;
495         private boolean mIsInterfaceRecovered;
496 
setHostnameConfiguration(final boolean enable, final String hostname)497         public void setHostnameConfiguration(final boolean enable, final String hostname) {
498             mIsHostnameConfigurationEnabled = enable;
499             mHostname = hostname;
500         }
501 
502         // Enable this flag to simulate the interface has been added back after removing
503         // on the provisioning start. However, the actual tap interface has been removed,
504         // interface parameters query will get null when attempting to restore Interface
505         // MTU. Create a new InterfaceParams instance and return instead just for interface
506         // toggling test case.
simulateInterfaceRecover()507         public void simulateInterfaceRecover() {
508             mIsInterfaceRecovered = true;
509         }
510 
511         @Override
getInterfaceParams(String ifname)512         public InterfaceParams getInterfaceParams(String ifname) {
513             return mIsInterfaceRecovered
514                     ? new InterfaceParams(ifname, 1 /* index */,
515                             MacAddress.fromString("00:11:22:33:44:55"))
516                     : super.getInterfaceParams(ifname);
517         }
518 
519         @Override
getNetd(Context context)520         public INetd getNetd(Context context) {
521             return mNetd;
522         }
523 
524         @Override
getIpMemoryStore(Context context, NetworkStackServiceManager nssManager)525         public NetworkStackIpMemoryStore getIpMemoryStore(Context context,
526                 NetworkStackServiceManager nssManager) {
527             return mIpMemoryStore;
528         }
529 
530         @Override
makeDhcpClient(Context context, StateMachine controller, InterfaceParams ifParams, DhcpClient.Dependencies deps)531         public DhcpClient makeDhcpClient(Context context, StateMachine controller,
532                 InterfaceParams ifParams, DhcpClient.Dependencies deps) {
533             mDhcpClient = DhcpClient.makeDhcpClient(context, controller, ifParams, deps);
534             return mDhcpClient;
535         }
536 
537         @Override
makeDhcp6Client(Context context, StateMachine controller, InterfaceParams ifParams, Dhcp6Client.Dependencies deps)538         public Dhcp6Client makeDhcp6Client(Context context, StateMachine controller,
539                 InterfaceParams ifParams, Dhcp6Client.Dependencies deps) {
540             mDhcp6Client = Dhcp6Client.makeDhcp6Client(context, controller, ifParams, deps);
541             return mDhcp6Client;
542         }
543 
544         @Override
getIpReachabilityMonitor(Context context, InterfaceParams ifParams, Handler h, SharedLog log, IpReachabilityMonitor.Callback callback, boolean usingMultinetworkPolicyTracker, IpReachabilityMonitor.Dependencies deps, final INetd netd)545         public IpReachabilityMonitor getIpReachabilityMonitor(Context context,
546                 InterfaceParams ifParams, Handler h, SharedLog log,
547                 IpReachabilityMonitor.Callback callback, boolean usingMultinetworkPolicyTracker,
548                 IpReachabilityMonitor.Dependencies deps, final INetd netd) {
549             return new IpReachabilityMonitor(context, ifParams, h, log, callback,
550                     usingMultinetworkPolicyTracker, deps, netd);
551         }
552 
553         @Override
isFeatureEnabled(final Context context, final String name)554         public boolean isFeatureEnabled(final Context context, final String name) {
555             return IpClientIntegrationTestCommon.this.isFeatureEnabled(name);
556         }
557 
558         @Override
isFeatureNotChickenedOut(final Context context, final String name)559         public boolean isFeatureNotChickenedOut(final Context context, final String name) {
560             return IpClientIntegrationTestCommon.this.isFeatureNotChickenedOut(name);
561         }
562 
563         @Override
getDeviceConfigPropertyInt(String name, int ignoredDefaultValue)564         public int getDeviceConfigPropertyInt(String name, int ignoredDefaultValue) {
565             // Default is never used because all device config properties must be mocked by test.
566             try {
567                 return Integer.parseInt(getDeviceConfigProperty(name));
568             } catch (NumberFormatException e) {
569                 throw new IllegalStateException("Non-mocked device config property " + name);
570             }
571         }
572 
573         @Override
getDhcp6ClientDependencies()574         public Dhcp6Client.Dependencies getDhcp6ClientDependencies() {
575             return new Dhcp6Client.Dependencies() {
576                 @Override
577                 public int getDeviceConfigPropertyInt(String name, int defaultValue) {
578                     return Dependencies.this.getDeviceConfigPropertyInt(name,
579                             defaultValue);
580                 }
581             };
582         }
583 
584         @Override
getDhcpClientDependencies( NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics)585         public DhcpClient.Dependencies getDhcpClientDependencies(
586                 NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) {
587             return new DhcpClient.Dependencies(ipMemoryStore, metrics) {
588                 @Override
589                 public boolean isFeatureEnabled(final Context context, final String name) {
590                     return Dependencies.this.isFeatureEnabled(context, name);
591                 }
592 
593                 @Override
594                 public boolean isFeatureNotChickenedOut(final Context context, final String name) {
595                     return Dependencies.this.isFeatureNotChickenedOut(context, name);
596                 }
597 
598                 @Override
599                 public int getIntDeviceConfig(final String name, int minimumValue,
600                         int maximumValue, int defaultValue) {
601                     return Dependencies.this.getDeviceConfigPropertyInt(name, defaultValue);
602                 }
603 
604                 @Override
605                 public int getIntDeviceConfig(final String name, int defaultValue) {
606                     return Dependencies.this.getDeviceConfigPropertyInt(name, defaultValue);
607                 }
608 
609                 @Override
610                 public PowerManager.WakeLock getWakeLock(final PowerManager powerManager) {
611                     return mTimeoutWakeLock;
612                 }
613 
614                 @Override
615                 public boolean getSendHostnameOverlaySetting(final Context context) {
616                     return mIsHostnameConfigurationEnabled;
617                 }
618 
619                 @Override
620                 public String getDeviceName(final Context context) {
621                     return mHostname;
622                 }
623             };
624         }
625 
626         @Override
627         public IpReachabilityMonitor.Dependencies getIpReachabilityMonitorDeps(Context context,
628                 String name) {
629             return new IpReachabilityMonitor.Dependencies() {
630                 public void acquireWakeLock(long durationMs) {
631                     // It doesn't matter for the integration test app on whether the wake lock
632                     // is acquired or not.
633                     return;
634                 }
635 
636                 public IpNeighborMonitor makeIpNeighborMonitor(Handler h, SharedLog log,
637                         NeighborEventConsumer cb) {
638                     return new IpNeighborMonitor(h, log, cb);
639                 }
640 
641                 public boolean isFeatureEnabled(final Context context, final String name) {
642                     return Dependencies.this.isFeatureEnabled(context, name);
643                 }
644 
645                 public boolean isFeatureNotChickenedOut(final Context context, final String name) {
646                     return Dependencies.this.isFeatureNotChickenedOut(context, name);
647                 }
648 
649                 public IpReachabilityMonitorMetrics getIpReachabilityMonitorMetrics() {
650                     return mIpReachabilityMonitorMetrics;
651                 }
652             };
653         }
654 
655         @Override
656         public NetworkQuirkMetrics getNetworkQuirkMetrics() {
657             return new NetworkQuirkMetrics(mNetworkQuirkMetricsDeps);
658         }
659     }
660 
661     @NonNull
662     protected abstract IIpClient makeIIpClient(
663             @NonNull String ifaceName, @NonNull IIpClientCallbacks cb);
664 
665     // In production. features are enabled if the flag is lower than the package version.
666     // For testing, we can just use 1 for enabled and -1 for disabled or chickened out.
667     static final String FEATURE_ENABLED = "1";
668     static final String FEATURE_DISABLED = "-1";
669 
670     final void setFeatureEnabled(String feature, boolean enabled) {
671         setDeviceConfigProperty(feature, enabled ? FEATURE_ENABLED : FEATURE_DISABLED);
672     }
673 
674     final void setFeatureChickenedOut(String feature, boolean chickenedOut) {
675         setDeviceConfigProperty(feature, chickenedOut ? FEATURE_DISABLED : FEATURE_ENABLED);
676     }
677 
678     final void setDeviceConfigProperty(String name, int value) {
679         setDeviceConfigProperty(name, Integer.toString(value));
680     }
681 
682     protected abstract void setDeviceConfigProperty(String name, String value);
683 
684     protected abstract String getDeviceConfigProperty(String name);
685 
686     protected abstract boolean isFeatureEnabled(String name);
687 
688     protected abstract boolean isFeatureNotChickenedOut(String name);
689 
690     protected abstract boolean useNetworkStackSignature();
691 
692     protected abstract NetworkAttributes getStoredNetworkAttributes(String l2Key, long timeout);
693 
694     protected abstract void storeNetworkAttributes(String l2Key, NetworkAttributes na);
695 
696     protected abstract void assertIpMemoryNeverStoreNetworkAttributes(String l2Key, long timeout);
697 
698     protected abstract int readNudSolicitNumInSteadyStateFromResource();
699 
700     protected abstract int readNudSolicitNumPostRoamingFromResource();
701 
702     protected abstract void storeNetworkEvent(String cluster, long now, long expiry, int eventType);
703 
704     protected abstract int[] getStoredNetworkEventCount(String cluster, long[] sinceTimes,
705             int[] eventType, long timeout);
706 
707     protected final boolean testSkipped() {
708         if (!useNetworkStackSignature() && !TestNetworkStackServiceClient.isSupported()) {
709             fail("Device running root tests doesn't support TestNetworkStackServiceClient.");
710         }
711         return !useNetworkStackSignature() && mIsSignatureRequiredTest;
712     }
713 
714     private static InetAddress ipAddr(String addr) {
715         return InetAddresses.parseNumericAddress(addr);
716     }
717 
718     private static Inet4Address ipv4Addr(String addr) {
719         return (Inet4Address) ipAddr(addr);
720     }
721 
722     private static Inet6Address ipv6Addr(String addr) {
723         return (Inet6Address) ipAddr(addr);
724     }
725 
726     private void setDhcpFeatures(final boolean isRapidCommitEnabled,
727             final boolean isDhcpIpConflictDetectEnabled) {
728         setFeatureEnabled(NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION, isRapidCommitEnabled);
729         setFeatureEnabled(NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION,
730                 isDhcpIpConflictDetectEnabled);
731     }
732 
733     private void setDeviceConfigForMaxDtimMultiplier() {
734         setDeviceConfigProperty(IpClient.CONFIG_INITIAL_PROVISIONING_DTIM_DELAY_MS,
735                 500 /* default value */);
736         setDeviceConfigProperty(IpClient.CONFIG_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER,
737                 IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER);
738         setDeviceConfigProperty(IpClient.CONFIG_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER,
739                 IpClient.DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
740         setDeviceConfigProperty(IpClient.CONFIG_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER,
741                 IpClient.DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
742         setDeviceConfigProperty(IpClient.CONFIG_DUAL_STACK_MAX_DTIM_MULTIPLIER,
743                 IpClient.DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER);
744         setDeviceConfigProperty(IpClient.CONFIG_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER,
745                 IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
746     }
747 
748     @Before
749     public void setUp() throws Exception {
750         final String testMethodName = mTestNameRule.getMethodName();
751         final Method testMethod = IpClientIntegrationTestCommon.class.getMethod(testMethodName);
752         mIsSignatureRequiredTest = testMethod.getAnnotation(SignatureRequiredTest.class) != null;
753         assumeFalse(testSkipped());
754 
755         // Enable DHCPv6 Prefix Delegation.
756         setFeatureEnabled(NetworkStackUtils.IPCLIENT_DHCPV6_PREFIX_DELEGATION_VERSION,
757                 true /* isDhcp6PrefixDelegationEnabled */);
758 
759         // Set flags based on test method annotations.
760         final Flag[] flags = testMethod.getAnnotationsByType(Flag.class);
761         for (Flag flag : flags) {
762             setFeatureEnabled(flag.name(), flag.enabled());
763         }
764 
765         setUpTapInterface();
766         // It turns out that Router Solicitation will also be sent out even after the tap interface
767         // is brought up, however, we want to wait for RS which is sent due to IPv6 stack is enabled
768         // in the test code. The early RS might bring kind of race, for example, the IPv6 stack has
769         // not been enabled when test code sees the RS, then kernel will not process RA even if we
770         // replies immediately after receiving RS. Always waiting for the first RS show up after
771         // interface is brought up helps prevent the race.
772         waitForRouterSolicitation();
773 
774         mCb = mock(IIpClientCallbacks.class);
775         if (useNetworkStackSignature()) {
776             setUpMocks();
777             setUpIpClient();
778             // Enable packet retransmit alarm in DhcpClient.
779             enableRealAlarm("DhcpClient." + mIfaceName + ".KICK");
780             enableRealAlarm("DhcpClient." + mIfaceName + ".RENEW");
781             // Enable alarm for IPv6 autoconf via SLAAC in IpClient.
782             enableRealAlarm("IpClient." + mIfaceName + ".EVENT_IPV6_AUTOCONF_TIMEOUT");
783             // Enable packet retransmit alarm in Dhcp6Client.
784             enableRealAlarm("Dhcp6Client." + mIfaceName + ".KICK");
785         }
786 
787         mIIpClient = makeIIpClient(mIfaceName, mCb);
788 
789         // Enable multicast filtering after creating IpClient instance, make the integration test
790         // more realistic.
791         mIIpClient.setMulticastFilter(true);
792         setDeviceConfigForMaxDtimMultiplier();
793         // Set IPv6 autoconf timeout. For signature tests, it has disabled the provisioning delay,
794         // use a small timeout value to speed up the test execution; For root tests, we have to
795         // wait a bit longer to make sure that we do see the success IPv6 provisioning, otherwise,
796         // the global IPv6 address may show up later due to DAD, so we consider that autoconf fails
797         // in this case and start DHCPv6 Prefix Delegation then.
798         final int timeout = useNetworkStackSignature() ? 500 : (int) TEST_TIMEOUT_MS;
799         setDeviceConfigProperty(IpClient.CONFIG_IPV6_AUTOCONF_TIMEOUT, timeout /* default value */);
800         // Set DHCP minimum lease.
801         setDeviceConfigProperty(DhcpPacket.CONFIG_MINIMUM_LEASE, DhcpPacket.DEFAULT_MINIMUM_LEASE);
802     }
803 
804     protected void setUpMocks() throws Exception {
805         MockitoAnnotations.initMocks(this);
806 
807         mDependencies = new Dependencies();
808         when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarm);
809         when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mCm);
810         when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
811                 .thenReturn(mDevicePolicyManager);
812         when(mContext.getPackageManager()).thenReturn(mPackageManager);
813         when(mContext.getResources()).thenReturn(mResources);
814         when(mResources.getInteger(eq(R.integer.config_nud_postroaming_solicit_num))).thenReturn(5);
815         when(mResources.getInteger(eq(R.integer.config_nud_postroaming_solicit_interval)))
816                  .thenReturn(750);
817         when(mResources.getInteger(eq(R.integer.config_nud_steadystate_solicit_num)))
818                  .thenReturn(10);
819         when(mResources.getInteger(eq(R.integer.config_nud_steadystate_solicit_interval)))
820                  .thenReturn(750);
821         when(mContext.getContentResolver()).thenReturn(mContentResolver);
822         when(mNetworkStackServiceManager.getIpMemoryStoreService())
823                 .thenReturn(mIpMemoryStoreService);
824         when(mCb.getInterfaceVersion()).thenReturn(IpClient.VERSION_ADDED_REACHABILITY_FAILURE);
825         // This mock is required, otherwise, ignoreIPv6ProvisioningLoss variable is always true,
826         // and IpReachabilityMonitor#avoidingBadLinks() will always return false as well, that
827         // results in the target tested IPv6 off-link DNS server won't be removed from LP and
828         // notifyLost won't be invoked, or the wrong code path when receiving RA with 0 router
829         // liftime.
830         when(mCm.shouldAvoidBadWifi()).thenReturn(true);
831 
832         when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(
833                 new ComponentName(TEST_DEVICE_OWNER_APP_PACKAGE, "com.example.SomeClass"));
834         when(mPackageManager.getPackagesForUid(TEST_DEVICE_OWNER_APP_UID)).thenReturn(
835                 new String[] { TEST_DEVICE_OWNER_APP_PACKAGE });
836 
837         // Retrieve the network event count.
838         doAnswer(invocation -> {
839             final String cluster = invocation.getArgument(0);
840             final long[] sinceTimes = invocation.getArgument(1);
841             final int[] eventType = invocation.getArgument(2);
842             ((OnNetworkEventCountRetrievedListener) invocation.getArgument(3))
843                     .onNetworkEventCountRetrieved(
844                             new Status(SUCCESS),
845                             getStoredNetworkEventCount(cluster, sinceTimes, eventType,
846                                     0 /* timeout not used */));
847             return null;
848         }).when(mIpMemoryStore).retrieveNetworkEventCount(eq(TEST_CLUSTER), any(), any(), any());
849 
850         setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10);
851         setDeviceConfigProperty(DhcpClient.ARP_FIRST_PROBE_DELAY_MS, 10);
852         setDeviceConfigProperty(DhcpClient.ARP_PROBE_MIN_MS, 10);
853         setDeviceConfigProperty(DhcpClient.ARP_PROBE_MAX_MS, 20);
854         setDeviceConfigProperty(DhcpClient.ARP_FIRST_ANNOUNCE_DELAY_MS, 10);
855         setDeviceConfigProperty(DhcpClient.ARP_ANNOUNCE_INTERVAL_MS, 10);
856 
857         // Set the initial netlink socket receive buffer size to a minimum of 100KB to ensure test
858         // cases are still working, meanwhile in order to easily overflow the receive buffer by
859         // sending as few RAs as possible for test case where it's used to verify ENOBUFS.
860         setDeviceConfigProperty(CONFIG_SOCKET_RECV_BUFSIZE, 100 * 1024);
861 
862         // Set the timeout to wait IPv6 autoconf to complete.
863         setDeviceConfigProperty(CONFIG_IPV6_AUTOCONF_TIMEOUT, 500);
864 
865         // Set the minimal RA lifetime value, any RA section with liftime below this value will be
866         // ignored.
867         setDeviceConfigProperty(CONFIG_ACCEPT_RA_MIN_LFT, 67);
868 
869         // Set the polling interval to update APF data snapshot.
870         setDeviceConfigProperty(CONFIG_APF_COUNTER_POLLING_INTERVAL_SECS,
871                 DEFAULT_APF_COUNTER_POLLING_INTERVAL_SECS);
872 
873         // Set the NUD failure event count daily and weekly thresholds.
874         setDeviceConfigProperty(CONFIG_NUD_FAILURE_COUNT_DAILY_THRESHOLD,
875                 DEFAULT_NUD_FAILURE_COUNT_DAILY_THRESHOLD);
876         setDeviceConfigProperty(CONFIG_NUD_FAILURE_COUNT_WEEKLY_THRESHOLD,
877                 DEFAULT_NUD_FAILURE_COUNT_WEEKLY_THRESHOLD);
878     }
879 
880     private void awaitIpClientShutdown() throws Exception {
881         verify(mCb, timeout(TEST_TIMEOUT_MS)).onQuit();
882     }
883 
884     @After
885     public void tearDown() throws Exception {
886         if (testSkipped()) return;
887         if (mNetworkAgent != null) {
888             mNetworkAgent.unregister();
889         }
890         if (mNetworkAgentThread != null) {
891             mNetworkAgentThread.quitSafely();
892             mNetworkAgentThread.join();
893         }
894         teardownTapInterface();
895         mIIpClient.shutdown();
896         awaitIpClientShutdown();
897     }
898 
899     private void setUpTapInterface() throws Exception {
900         final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
901         final TestNetworkInterface iface = runAsShell(MANAGE_TEST_NETWORKS, () -> {
902             final TestNetworkManager tnm =
903                     inst.getContext().getSystemService(TestNetworkManager.class);
904             try {
905                 return tnm.createTapInterface(true /* carrierUp */, true /* bringUp */,
906                         true /* disableIpv6ProvisioningDelay */);
907             } catch (NoSuchMethodError e) {
908                 // createTapInterface(boolean, boolean, boolean) has been introduced since T,
909                 // use the legancy API if the method is not found on previous platforms.
910                 return tnm.createTapInterface();
911             }
912         });
913         mIfaceName = iface.getInterfaceName();
914         mClientMac = getIfaceMacAddr(mIfaceName).toByteArray();
915         mPacketReaderThread = new HandlerThread(
916                 IpClientIntegrationTestCommon.class.getSimpleName());
917         mPacketReaderThread.start();
918         mHandler = mPacketReaderThread.getThreadHandler();
919 
920         // Detach the FileDescriptor from the ParcelFileDescriptor.
921         // Otherwise, the garbage collector might call the ParcelFileDescriptor's finalizer, which
922         // closes the FileDescriptor and destroys our tap interface. An alternative would be to
923         // make the ParcelFileDescriptor or the TestNetworkInterface a class member so they never
924         // go out of scope.
925         mTapFd = new FileDescriptor();
926         mTapFd.setInt$(iface.getFileDescriptor().detachFd());
927         mPacketReader = new PollPacketReader(mHandler, mTapFd, DATA_BUFFER_LEN);
928         mHandler.post(() -> mPacketReader.start());
929     }
930 
931     private TestNetworkInterface setUpClatInterface(@NonNull String baseIface) throws Exception {
932         final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
933         final TestNetworkInterface iface = runAsShell(MANAGE_TEST_NETWORKS, () -> {
934             final TestNetworkManager tnm =
935                     inst.getContext().getSystemService(TestNetworkManager.class);
936             return tnm.createTapInterface(false /* bringUp */, CLAT_PREFIX + baseIface);
937         });
938         return iface;
939     }
940 
941     private void teardownTapInterface() throws Exception {
942         if (mPacketReader != null) {
943             mHandler.post(() -> mPacketReader.stop());  // Also closes the socket
944             mTapFd = null;
945         }
946         if (mPacketReaderThread != null) {
947             mPacketReaderThread.quitSafely();
948             mPacketReaderThread.join();
949         }
950     }
951 
952     private MacAddress getIfaceMacAddr(String ifaceName) throws IOException {
953         // InterfaceParams.getByName requires CAP_NET_ADMIN: read the mac address with the shell
954         final String strMacAddr = getOneLineCommandOutput(
955                 "su root cat /sys/class/net/" + ifaceName + "/address");
956         return MacAddress.fromString(strMacAddr);
957     }
958 
959     private String getOneLineCommandOutput(String cmd) throws IOException {
960         try (ParcelFileDescriptor fd = InstrumentationRegistry.getInstrumentation()
961                 .getUiAutomation().executeShellCommand(cmd);
962              BufferedReader reader = new BufferedReader(new FileReader(fd.getFileDescriptor()))) {
963             return reader.readLine();
964         }
965     }
966 
967     private void enableRealAlarm(String cmdName) {
968         doAnswer((inv) -> {
969             final Context context = InstrumentationRegistry.getTargetContext();
970             final AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
971             alarmManager.setExact(inv.getArgument(0), inv.getArgument(1), inv.getArgument(2),
972                     inv.getArgument(3), inv.getArgument(4));
973             return null;
974         }).when(mAlarm).setExact(anyInt(), anyLong(), eq(cmdName), any(OnAlarmListener.class),
975                 any(Handler.class));
976     }
977 
978     private IpClient makeIpClient() throws Exception {
979         IpClient ipc =
980                 new IpClient(mContext, mIfaceName, mCb, mNetworkStackServiceManager, mDependencies);
981         // Wait for IpClient to enter its initial state. Otherwise, additional setup steps or tests
982         // that mock IpClient's dependencies might interact with those mocks while IpClient is
983         // starting. This would cause UnfinishedStubbingExceptions as mocks cannot be interacted
984         // with while they are being stubbed.
985         HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
986         return ipc;
987     }
988 
989     private void setUpIpClient() throws Exception {
990         final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
991         final IBinder netdIBinder =
992                 (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE);
993         mNetd = spy(INetd.Stub.asInterface(netdIBinder));
994         when(mContext.getSystemService(eq(Context.NETD_SERVICE))).thenReturn(netdIBinder);
995         assertNotNull(mNetd);
996 
997         mIpc = makeIpClient();
998 
999         // Tell the IpMemoryStore immediately to answer any question about network attributes with a
1000         // null response. Otherwise, the DHCP client will wait for two seconds before starting,
1001         // while its query to the IpMemoryStore times out.
1002         // This does not affect any test that makes the mock memory store return results, because
1003         // unlike when(), it is documented that doAnswer() can be called more than once, to change
1004         // the behaviour of a mock in the middle of a test.
1005         doAnswer(invocation -> {
1006             final String l2Key = invocation.getArgument(0);
1007             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
1008                     .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null);
1009             return null;
1010         }).when(mIpMemoryStore).retrieveNetworkAttributes(any(), any());
1011 
1012         disableIpv6ProvisioningDelays();
1013     }
1014 
1015     private <T> T verifyWithTimeout(InOrder inOrder, T t) {
1016         if (inOrder != null) {
1017             return inOrder.verify(t, timeout(TEST_TIMEOUT_MS));
1018         } else {
1019             return verify(t, timeout(TEST_TIMEOUT_MS));
1020         }
1021     }
1022 
1023     private void expectAlarmCancelled(InOrder inOrder, OnAlarmListener listener) {
1024         inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).cancel(eq(listener));
1025     }
1026 
1027     private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, long afterSeconds,
1028             Handler handler) {
1029         // Allow +/- 3 seconds to prevent flaky tests.
1030         final long when = SystemClock.elapsedRealtime() + afterSeconds * 1000;
1031         final long min = when - 3 * 1000;
1032         final long max = when + 3 * 1000;
1033         ArgumentCaptor<OnAlarmListener> captor = ArgumentCaptor.forClass(OnAlarmListener.class);
1034         verifyWithTimeout(inOrder, mAlarm).setExact(
1035                 anyInt(), longThat(x -> x >= min && x <= max),
1036                 contains(tagMatch), captor.capture(), eq(handler));
1037         return captor.getValue();
1038     }
1039 
1040     private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, int afterSeconds) {
1041         return expectAlarmSet(inOrder, tagMatch, (long) afterSeconds, mIpc.getHandler());
1042     }
1043 
1044     private boolean packetContainsExpectedField(final byte[] packet, final int offset,
1045             final byte[] expected) {
1046         if (packet.length < offset + expected.length) return false;
1047         for (int i = 0; i < expected.length; ++i) {
1048             if (packet[offset + i] != expected[i]) return false;
1049         }
1050         return true;
1051     }
1052 
1053     private boolean isDhcpPacket(final byte[] packet) {
1054         final ByteBuffer buffer = ByteBuffer.wrap(packet);
1055 
1056         // check the packet length
1057         if (packet.length < DHCP_HEADER_OFFSET) return false;
1058 
1059         // check the source port and dest port in UDP header
1060         buffer.position(UDP_SRC_PORT_OFFSET);
1061         final short udpSrcPort = buffer.getShort();
1062         final short udpDstPort = buffer.getShort();
1063         if (udpSrcPort != DHCP_CLIENT || udpDstPort != DHCP_SERVER) return false;
1064 
1065         // check DHCP message type
1066         buffer.position(DHCP_MESSAGE_OP_CODE_OFFSET);
1067         final byte dhcpOpCode = buffer.get();
1068         if (dhcpOpCode != DHCP_BOOTREQUEST) return false;
1069 
1070         // check DHCP magic cookie
1071         buffer.position(DHCP_OPTION_MAGIC_COOKIE_OFFSET);
1072         final int dhcpMagicCookie = buffer.getInt();
1073         if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) return false;
1074 
1075         return true;
1076     }
1077 
1078     private boolean isDhcp6Packet(final byte[] packet) {
1079         final ByteBuffer buffer = ByteBuffer.wrap(packet);
1080 
1081         // check the packet length
1082         if (packet.length < DHCP6_HEADER_OFFSET) return false;
1083 
1084         // check Ethernet header
1085         final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buffer);
1086         if (ethHdr.etherType != ETH_P_IPV6) {
1087             return false;
1088         }
1089 
1090         // check IPv6 header
1091         final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buffer);
1092         final int version = (ipv6Hdr.vtf >> 28) & 0x0F;
1093         if (version != 6) {
1094             return false;
1095         }
1096         if (ipv6Hdr.nextHeader != IPPROTO_UDP) {
1097             return false;
1098         }
1099         if (!ipv6Hdr.dstIp.equals(ALL_DHCP_RELAY_AGENTS_AND_SERVERS)) {
1100             return false;
1101         }
1102         mClientIpAddress = ipv6Hdr.srcIp;
1103 
1104         // check the source port and dest port in UDP header
1105         final short udpSrcPort = buffer.getShort();
1106         final short udpDstPort = buffer.getShort();
1107         return (udpSrcPort == DHCP6_CLIENT_PORT && udpDstPort == DHCP6_SERVER_PORT);
1108     }
1109 
1110     private ArpPacket parseArpPacketOrNull(final byte[] packet) {
1111         try {
1112             return ArpPacket.parseArpPacket(packet, packet.length);
1113         } catch (ArpPacket.ParseException e) {
1114             return null;
1115         }
1116     }
1117 
1118     private NeighborAdvertisement parseNeighborAdvertisementOrNull(final byte[] packet) {
1119         try {
1120             return NeighborAdvertisement.parse(packet, packet.length);
1121         } catch (NeighborAdvertisement.ParseException e) {
1122             return null;
1123         }
1124     }
1125 
1126     private NeighborSolicitation parseNeighborSolicitationOrNull(final byte[] packet) {
1127         try {
1128             return NeighborSolicitation.parse(packet, packet.length);
1129         } catch (NeighborSolicitation.ParseException e) {
1130             return null;
1131         }
1132     }
1133 
1134     private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet,
1135             final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
1136             final String captivePortalUrl, final Integer ipv6OnlyWaitTime,
1137             final String domainName, final List<String> domainSearchList) {
1138         return DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
1139                 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
1140                 clientAddress /* yourIp */, packet.getClientMac(), leaseTimeSec,
1141                 NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */,
1142                 Collections.singletonList(SERVER_ADDR) /* gateways */,
1143                 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
1144                 SERVER_ADDR /* dhcpServerIdentifier */, domainName, HOSTNAME,
1145                 false /* metered */, mtu, captivePortalUrl, ipv6OnlyWaitTime, domainSearchList);
1146     }
1147 
1148     private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet,
1149             final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
1150             final String captivePortalUrl) {
1151         return buildDhcpOfferPacket(packet, clientAddress, leaseTimeSec, mtu, captivePortalUrl,
1152                 null /* ipv6OnlyWaitTime */, null /* domainName */, null /* domainSearchList */);
1153     }
1154 
1155     private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet,
1156             final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
1157             final boolean rapidCommit, final String captivePortalApiUrl,
1158             final Integer ipv6OnlyWaitTime, final String domainName,
1159             final List<String> domainSearchList) {
1160         return DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
1161                 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
1162                 clientAddress /* yourIp */, CLIENT_ADDR /* requestIp */, packet.getClientMac(),
1163                 leaseTimeSec, NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */,
1164                 Collections.singletonList(SERVER_ADDR) /* gateways */,
1165                 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
1166                 SERVER_ADDR /* dhcpServerIdentifier */, domainName, HOSTNAME,
1167                 false /* metered */, mtu, rapidCommit, captivePortalApiUrl, ipv6OnlyWaitTime,
1168                 domainSearchList);
1169     }
1170 
1171     private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet,
1172             final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
1173             final boolean rapidCommit, final String captivePortalApiUrl) {
1174         return buildDhcpAckPacket(packet, clientAddress, leaseTimeSec, mtu, rapidCommit,
1175                 captivePortalApiUrl, null /* ipv6OnlyWaitTime */, null /* domainName */,
1176                 null /* domainSearchList */);
1177     }
1178 
1179     private static ByteBuffer buildDhcpNakPacket(final DhcpPacket packet, final String message) {
1180         return DhcpPacket.buildNakPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
1181             SERVER_ADDR /* serverIp */, INADDR_ANY /* relayIp */, packet.getClientMac(),
1182             false /* broadcast */, message);
1183     }
1184 
1185     private static ByteBuffer buildDhcp6Packet(final ByteBuffer payload, final MacAddress clientMac,
1186             final Inet6Address clientIp) throws Exception {
1187         final ByteBuffer buffer = PacketBuilder.allocate(true /* hasEther */, IPPROTO_IPV6,
1188                 IPPROTO_UDP, payload.limit());
1189         final PacketBuilder pb = new PacketBuilder(buffer);
1190 
1191         pb.writeL2Header(ROUTER_MAC /* srcMac */, clientMac /* dstMac */, (short) ETH_P_IPV6);
1192         pb.writeIpv6Header(0x60000000 /* version=6, traffic class=0, flow label=0 */,
1193                 (byte) IPPROTO_UDP, (short) 64 /* hop limit */, ROUTER_LINK_LOCAL /* srcIp */,
1194                 clientIp /* dstIp */);
1195         pb.writeUdpHeader((short) DHCP6_SERVER_PORT /*src port */,
1196                 (short) DHCP6_CLIENT_PORT /* dst port */);
1197         buffer.put(payload);
1198         return pb.finalizePacket();
1199     }
1200 
1201     private static ByteBuffer buildDhcp6Advertise(final Dhcp6Packet solicit, final byte[] iapd,
1202             final byte[] clientMac, final Inet6Address clientIp) throws Exception {
1203         final ByteBuffer advertise = Dhcp6Packet.buildAdvertisePacket(solicit.getTransactionId(),
1204                 iapd, solicit.getClientDuid(), ROUTER_DUID);
1205         return buildDhcp6Packet(advertise, MacAddress.fromBytes(clientMac), clientIp);
1206     }
1207 
1208     private static ByteBuffer buildDhcp6Reply(final Dhcp6Packet request, final byte[] iapd,
1209             final byte[] clientMac, final Inet6Address clientIp, boolean rapidCommit)
1210             throws Exception {
1211         final ByteBuffer reply = Dhcp6Packet.buildReplyPacket(request.getTransactionId(),
1212                 iapd, request.getClientDuid(), ROUTER_DUID, rapidCommit);
1213         return buildDhcp6Packet(reply, MacAddress.fromBytes(clientMac), clientIp);
1214     }
1215 
1216     private void sendArpReply(final byte[] dstMac, final byte[] srcMac, final Inet4Address targetIp,
1217             final Inet4Address senderIp) throws IOException {
1218         final ByteBuffer packet = ArpPacket.buildArpPacket(dstMac, srcMac, targetIp.getAddress(),
1219                 dstMac /* target HW address */, senderIp.getAddress(), (short) ARP_REPLY);
1220         mPacketReader.sendResponse(packet);
1221     }
1222 
1223     private void sendArpProbe() throws IOException {
1224         final ByteBuffer packet = ArpPacket.buildArpPacket(DhcpPacket.ETHER_BROADCAST /* dst */,
1225                 ROUTER_MAC_BYTES /* srcMac */, CLIENT_ADDR.getAddress() /* target IP */,
1226                 new byte[ETHER_ADDR_LEN] /* target HW address */,
1227                 INADDR_ANY.getAddress() /* sender IP */, (short) ARP_REQUEST);
1228         mPacketReader.sendResponse(packet);
1229     }
1230 
1231     private void startIpClientProvisioning(final ProvisioningConfiguration cfg) throws Exception {
1232         mIIpClient.startProvisioning(cfg.toStableParcelable());
1233     }
1234 
1235     private void startIpClientProvisioning(final boolean shouldReplyRapidCommitAck,
1236             final boolean isPreconnectionEnabled,
1237             final boolean isDhcpIpConflictDetectEnabled,
1238             final String displayName,
1239             final ScanResultInfo scanResultInfo,
1240             final Layer2Information layer2Info)
1241                     throws Exception {
1242         ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
1243                 .withoutIpReachabilityMonitor()
1244                 .withLayer2Information(layer2Info == null
1245                         ? new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
1246                               MacAddress.fromString(TEST_DEFAULT_BSSID))
1247                         : layer2Info)
1248                 .withoutIPv6();
1249         if (isPreconnectionEnabled) prov.withPreconnection();
1250         if (displayName != null) prov.withDisplayName(displayName);
1251         if (scanResultInfo != null) prov.withScanResultInfo(scanResultInfo);
1252 
1253         setDhcpFeatures(shouldReplyRapidCommitAck, isDhcpIpConflictDetectEnabled);
1254 
1255         startIpClientProvisioning(prov.build());
1256         if (!isPreconnectionEnabled) {
1257             verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true);
1258         }
1259         verify(mCb, never()).onProvisioningFailure(any());
1260     }
1261 
1262     private void startIpClientProvisioning(final boolean isDhcpRapidCommitEnabled,
1263             final boolean isPreconnectionEnabled,
1264             final boolean isDhcpIpConflictDetectEnabled) throws Exception {
1265         startIpClientProvisioning(isDhcpRapidCommitEnabled,
1266                 isPreconnectionEnabled, isDhcpIpConflictDetectEnabled,
1267                 null /* displayName */, null /* ScanResultInfo */, null /* layer2Info */);
1268     }
1269 
1270     private void assertIpMemoryStoreNetworkAttributes(final Integer leaseTimeSec,
1271             final long startTime, final int mtu) {
1272         final NetworkAttributes na = getStoredNetworkAttributes(TEST_L2KEY, TEST_TIMEOUT_MS);
1273         assertNotNull(na);
1274         assertEquals(CLIENT_ADDR, na.assignedV4Address);
1275         if (leaseTimeSec == null || leaseTimeSec.intValue() == DhcpPacket.INFINITE_LEASE) {
1276             assertEquals(Long.MAX_VALUE, na.assignedV4AddressExpiry.longValue());
1277         } else {
1278             // check the lease expiry's scope
1279             final long upperBound = startTime + 7_200_000; // start timestamp + 2h
1280             final long lowerBound = startTime + 3_600_000; // start timestamp + 1h
1281             final long expiry = na.assignedV4AddressExpiry;
1282             assertTrue(upperBound > expiry);
1283             assertTrue(lowerBound < expiry);
1284         }
1285         assertEquals(Collections.singletonList(SERVER_ADDR), na.dnsAddresses);
1286         assertEquals(new Integer(mtu), na.mtu);
1287     }
1288 
1289     private void assertIpMemoryNeverStoreNetworkAttributes() {
1290         assertIpMemoryNeverStoreNetworkAttributes(TEST_L2KEY, TEST_TIMEOUT_MS);
1291     }
1292 
1293     private void assertHostname(final boolean expectSendHostname,
1294             final String hostname, final String hostnameAfterTransliteration,
1295             final List<DhcpPacket> packetList) throws Exception {
1296         for (DhcpPacket packet : packetList) {
1297             if (!expectSendHostname || hostname == null) {
1298                 assertNull(packet.getHostname());
1299             } else {
1300                 assertEquals(hostnameAfterTransliteration, packet.getHostname());
1301             }
1302         }
1303     }
1304 
1305     // Helper method to complete DHCP 2-way or 4-way handshake
1306     private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
1307             final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu,
1308             final boolean isDhcpIpConflictDetectEnabled,
1309             final String captivePortalApiUrl, final String displayName,
1310             final ScanResultInfo scanResultInfo, final Layer2Information layer2Info)
1311             throws Exception {
1312         startIpClientProvisioning(shouldReplyRapidCommitAck,
1313                 false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled,
1314                 displayName, scanResultInfo, layer2Info);
1315         return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu,
1316                 captivePortalApiUrl);
1317     }
1318 
1319     private List<DhcpPacket> handleDhcpPackets(final boolean isSuccessLease,
1320             final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu,
1321             final String captivePortalApiUrl) throws Exception {
1322         return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck,
1323                 mtu, captivePortalApiUrl, null /* ipv6OnlyWaitTime */,
1324                 null /* domainName */, null /* domainSearchList */);
1325     }
1326 
1327     private List<DhcpPacket> handleDhcpPackets(final boolean isSuccessLease,
1328             final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu,
1329             final String captivePortalApiUrl, final Integer ipv6OnlyWaitTime,
1330             final String domainName, final List<String> domainSearchList) throws Exception {
1331         final List<DhcpPacket> packetList = new ArrayList<>();
1332         DhcpPacket packet;
1333         while ((packet = getNextDhcpPacket()) != null) {
1334             packetList.add(packet);
1335             if (packet instanceof DhcpDiscoverPacket) {
1336                 if (shouldReplyRapidCommitAck) {
1337                     mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec,
1338                               (short) mtu, true /* rapidCommit */, captivePortalApiUrl,
1339                               ipv6OnlyWaitTime, domainName, domainSearchList));
1340                 } else {
1341                     mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR,
1342                             leaseTimeSec, (short) mtu, captivePortalApiUrl, ipv6OnlyWaitTime,
1343                             domainName, domainSearchList));
1344                 }
1345             } else if (packet instanceof DhcpRequestPacket) {
1346                 final ByteBuffer byteBuffer = isSuccessLease
1347                         ? buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec, (short) mtu,
1348                                 false /* rapidCommit */, captivePortalApiUrl, ipv6OnlyWaitTime,
1349                                 domainName, domainSearchList)
1350                         : buildDhcpNakPacket(packet, "duplicated request IP address");
1351                 mPacketReader.sendResponse(byteBuffer);
1352             } else {
1353                 fail("invalid DHCP packet");
1354             }
1355 
1356             // wait for reply to DHCPOFFER packet if disabling rapid commit option
1357             if (shouldReplyRapidCommitAck || !(packet instanceof DhcpDiscoverPacket)) {
1358                 return packetList;
1359             }
1360         }
1361         fail("No DHCPREQUEST received on interface");
1362         return packetList;
1363     }
1364 
1365     private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
1366             final Integer leaseTimeSec, final boolean isDhcpRapidCommitEnabled, final int mtu,
1367             final boolean isDhcpIpConflictDetectEnabled) throws Exception {
1368         return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpRapidCommitEnabled,
1369                 mtu, isDhcpIpConflictDetectEnabled,
1370                 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */,
1371                 null /* layer2Info */);
1372     }
1373 
1374     private List<DhcpPacket> performDhcpHandshake() throws Exception {
1375         return performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1376                 false /* shouldReplyRapidCommitAck */,
1377                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1378     }
1379 
1380     private DhcpPacket getNextDhcpPacket(final long timeout) throws Exception {
1381         byte[] packet;
1382         while ((packet = mDhcpPacketReadHead.getValue()
1383                 .poll(timeout, this::isDhcpPacket)) != null) {
1384             final DhcpPacket dhcpPacket = DhcpPacket.decodeFullPacket(packet, packet.length,
1385                     ENCAP_L2);
1386             if (dhcpPacket != null) return dhcpPacket;
1387         }
1388         return null;
1389     }
1390 
1391     private DhcpPacket getNextDhcpPacket() throws Exception {
1392         final DhcpPacket packet = getNextDhcpPacket(PACKET_TIMEOUT_MS);
1393         assertNotNull("No expected DHCP packet received on interface within timeout", packet);
1394         return packet;
1395     }
1396 
1397     private Dhcp6Packet getNextDhcp6Packet(final long timeout) throws Exception {
1398         byte[] packet;
1399         while ((packet = mDhcp6PacketReadHead.getValue()
1400                 .poll(timeout, this::isDhcp6Packet)) != null) {
1401             // Strip the Ethernet/IPv6/UDP headers, only keep DHCPv6 message payload for decode.
1402             final byte[] payload =
1403                     Arrays.copyOfRange(packet, DHCP6_HEADER_OFFSET, packet.length);
1404             final Dhcp6Packet dhcp6Packet = Dhcp6Packet.decode(payload, payload.length);
1405             if (dhcp6Packet != null) return dhcp6Packet;
1406         }
1407         return null;
1408     }
1409 
1410     private Dhcp6Packet getNextDhcp6Packet() throws Exception {
1411         final Dhcp6Packet packet = getNextDhcp6Packet(PACKET_TIMEOUT_MS);
1412         assertNotNull("No expected DHCPv6 packet received on interface within timeout", packet);
1413         return packet;
1414     }
1415 
1416     private DhcpPacket getReplyFromDhcpLease(final NetworkAttributes na, boolean timeout)
1417             throws Exception {
1418         doAnswer(invocation -> {
1419             if (timeout) return null;
1420             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
1421                     .onNetworkAttributesRetrieved(new Status(SUCCESS), TEST_L2KEY, na);
1422             return null;
1423         }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any());
1424         startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
1425                 false /* isPreconnectionEnabled */,
1426                 false /* isDhcpIpConflictDetectEnabled */);
1427         return getNextDhcpPacket();
1428     }
1429 
1430     private void removeTestInterface(final FileDescriptor fd) {
1431         try {
1432             Os.close(fd);
1433         } catch (ErrnoException e) {
1434             fail("Fail to close file descriptor: " + e);
1435         }
1436     }
1437 
1438     private void verifyAfterIpClientShutdown() throws RemoteException {
1439         final LinkProperties emptyLp = new LinkProperties();
1440         emptyLp.setInterfaceName(mIfaceName);
1441         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(emptyLp);
1442     }
1443 
1444     // Verify IPv4-only provisioning success. No need to verify IPv4 provisioning when below cases
1445     // happen:
1446     // 1. if there's a failure lease, onProvisioningSuccess() won't be called;
1447     // 2. if duplicated IPv4 address detection is enabled, verify TIMEOUT will affect ARP packets
1448     //    capture running in other test cases.
1449     // 3. if IPv6 is enabled, e.g. withoutIPv6() isn't called when starting provisioning.
1450     private LinkProperties verifyIPv4OnlyProvisioningSuccess(
1451             final Collection<InetAddress> addresses) throws Exception {
1452         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1453         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
1454         final LinkProperties lp = captor.getValue();
1455         assertNotNull(lp);
1456         assertNotEquals(0, lp.getDnsServers().size());
1457         assertEquals(addresses.size(), lp.getAddresses().size());
1458         assertTrue(lp.getAddresses().containsAll(addresses));
1459         assertTrue(hasRouteTo(lp, IPV4_TEST_SUBNET_PREFIX)); // IPv4 directly-connected route
1460         assertTrue(hasRouteTo(lp, IPV4_ANY_ADDRESS_PREFIX)); // IPv4 default route
1461         return lp;
1462     }
1463 
1464     private void doRestoreInitialMtuTest(final boolean shouldChangeMtu,
1465             final boolean shouldRemoveTestInterface) throws Exception {
1466         final long currentTime = System.currentTimeMillis();
1467         int mtu = TEST_DEFAULT_MTU;
1468 
1469         if (shouldChangeMtu) mtu = TEST_MIN_MTU;
1470         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1471                 false /* shouldReplyRapidCommitAck */,
1472                 mtu, false /* isDhcpIpConflictDetectEnabled */);
1473         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1474         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, mtu);
1475 
1476         if (shouldChangeMtu) {
1477             // Pretend that ConnectivityService set the MTU.
1478             mNetd.interfaceSetMtu(mIfaceName, mtu);
1479             assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), mtu);
1480         }
1481 
1482         // Sometimes, IpClient receives an update with an empty LinkProperties during startup,
1483         // when the link-local address is deleted after interface bringup. Reset expectations
1484         // here to ensure that verifyAfterIpClientShutdown does not fail because it sees two
1485         // empty LinkProperties changes instead of one.
1486         reset(mCb);
1487 
1488         if (shouldRemoveTestInterface) removeTestInterface(mTapFd);
1489         try {
1490             mIpc.shutdown();
1491             awaitIpClientShutdown();
1492             if (shouldRemoveTestInterface) {
1493                 verify(mNetd, never()).interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
1494             } else {
1495                 // Verify that MTU indeed has been restored or not.
1496                 verify(mNetd, times(shouldChangeMtu ? 1 : 0))
1497                         .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
1498             }
1499             verifyAfterIpClientShutdown();
1500         } catch (Exception e) {
1501             fail("Exception should not have been thrown after shutdown: " + e);
1502         }
1503     }
1504 
1505     private DhcpPacket assertDiscoverPacketOnPreconnectionStart() throws Exception {
1506         final ArgumentCaptor<List<Layer2PacketParcelable>> l2PacketList =
1507                 ArgumentCaptor.forClass(List.class);
1508 
1509         verify(mCb, timeout(TEST_TIMEOUT_MS)).onPreconnectionStart(l2PacketList.capture());
1510         final byte[] payload = l2PacketList.getValue().get(0).payload;
1511         DhcpPacket packet = DhcpPacket.decodeFullPacket(payload, payload.length, ENCAP_L2);
1512         assertTrue(packet instanceof DhcpDiscoverPacket);
1513         assertArrayEquals(INADDR_BROADCAST.getAddress(),
1514                 Arrays.copyOfRange(payload, IPV4_DST_ADDR_OFFSET, IPV4_DST_ADDR_OFFSET + 4));
1515         return packet;
1516     }
1517 
1518     private void doIpClientProvisioningWithPreconnectionTest(
1519             final boolean shouldReplyRapidCommitAck, final boolean shouldAbortPreconnection,
1520             final boolean shouldFirePreconnectionTimeout,
1521             final boolean timeoutBeforePreconnectionComplete) throws Exception {
1522         final long currentTime = System.currentTimeMillis();
1523         startIpClientProvisioning(shouldReplyRapidCommitAck,
1524                 true /* isDhcpPreConnectionEnabled */,
1525                 false /* isDhcpIpConflictDetectEnabled */);
1526         DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart();
1527         final int preconnDiscoverTransId = packet.getTransactionId();
1528 
1529         if (shouldAbortPreconnection) {
1530             if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) {
1531                 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
1532             }
1533 
1534             mIpc.notifyPreconnectionComplete(false /* abort */);
1535             HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1536 
1537             if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) {
1538                 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
1539             }
1540 
1541             // Either way should get DhcpClient go back to INIT state, and broadcast
1542             // DISCOVER with new transaction ID.
1543             packet = getNextDhcpPacket();
1544             assertTrue(packet instanceof DhcpDiscoverPacket);
1545             assertTrue(packet.getTransactionId() != preconnDiscoverTransId);
1546         } else if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) {
1547             // If timeout fires before success preconnection, DhcpClient will go back to INIT state,
1548             // and broadcast DISCOVER with new transaction ID.
1549             mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
1550             packet = getNextDhcpPacket();
1551             assertTrue(packet instanceof DhcpDiscoverPacket);
1552             assertTrue(packet.getTransactionId() != preconnDiscoverTransId);
1553             // any old response would be ignored due to mismatched transaction ID.
1554         }
1555 
1556         final short mtu = (short) TEST_DEFAULT_MTU;
1557         if (!shouldReplyRapidCommitAck) {
1558             mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR,
1559                     TEST_LEASE_DURATION_S, mtu, null /* captivePortalUrl */));
1560             packet = getNextDhcpPacket();
1561             assertTrue(packet instanceof DhcpRequestPacket);
1562         }
1563         mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, TEST_LEASE_DURATION_S,
1564                 mtu, shouldReplyRapidCommitAck, null /* captivePortalUrl */));
1565 
1566         if (!shouldAbortPreconnection) {
1567             mIpc.notifyPreconnectionComplete(true /* success */);
1568             HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
1569 
1570             // If timeout fires after successful preconnection, right now DhcpClient will have
1571             // already entered BOUND state, the delayed CMD_TIMEOUT command would be ignored. So
1572             // this case should be very rare, because the timeout alarm is cancelled when state
1573             // machine exits from Preconnecting state.
1574             if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) {
1575                 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
1576             }
1577         }
1578         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true);
1579         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1580         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1581     }
1582 
1583     private ArpPacket getNextArpPacket(final long timeout) throws Exception {
1584         byte[] packet;
1585         while ((packet = mArpPacketReadHead.getValue().poll(timeout, p -> true)) != null) {
1586             final ArpPacket arpPacket = parseArpPacketOrNull(packet);
1587             if (arpPacket != null) return arpPacket;
1588         }
1589         return null;
1590     }
1591 
1592     private ArpPacket getNextArpPacket() throws Exception {
1593         final ArpPacket packet = getNextArpPacket(PACKET_TIMEOUT_MS);
1594         assertNotNull("No expected ARP packet received on interface within timeout", packet);
1595         return packet;
1596     }
1597 
1598     private void assertArpPacket(final ArpPacket packet) {
1599         assertEquals(packet.opCode, ARP_REQUEST);
1600         assertEquals(packet.targetIp, CLIENT_ADDR);
1601         assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac));
1602     }
1603 
1604     private void assertArpProbe(final ArpPacket packet) {
1605         assertArpPacket(packet);
1606         assertEquals(packet.senderIp, INADDR_ANY);
1607     }
1608 
1609     private void assertArpAnnounce(final ArpPacket packet) {
1610         assertArpPacket(packet);
1611         assertEquals(packet.senderIp, CLIENT_ADDR);
1612     }
1613 
1614     private void assertArpRequest(final ArpPacket packet, final Inet4Address targetIp) {
1615         assertEquals(packet.opCode, ARP_REQUEST);
1616         assertEquals(packet.senderIp, CLIENT_ADDR);
1617         assertEquals(packet.targetIp, targetIp);
1618         assertTrue(Arrays.equals(packet.targetHwAddress.toByteArray(),
1619                 MacAddress.fromString("00:00:00:00:00:00").toByteArray()));
1620         assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac));
1621     }
1622 
1623     private void assertGratuitousARP(final ArpPacket packet) {
1624         assertEquals(packet.opCode, ARP_REPLY);
1625         assertEquals(packet.senderIp, CLIENT_ADDR);
1626         assertEquals(packet.targetIp, CLIENT_ADDR);
1627         assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac));
1628         assertTrue(Arrays.equals(packet.targetHwAddress.toByteArray(), ETHER_BROADCAST));
1629     }
1630 
1631     private void doIpAddressConflictDetectionTest(final boolean causeIpAddressConflict,
1632             final boolean shouldReplyRapidCommitAck, final boolean isDhcpIpConflictDetectEnabled,
1633             final boolean shouldResponseArpReply) throws Exception {
1634         final long currentTime = System.currentTimeMillis();
1635 
1636         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1637                 shouldReplyRapidCommitAck,
1638                 TEST_DEFAULT_MTU, isDhcpIpConflictDetectEnabled);
1639 
1640         // If we receive an ARP packet here, it's guaranteed to be from IP conflict detection,
1641         // because at this time the test interface does not have an IP address and therefore
1642         // won't send ARP for anything.
1643         if (causeIpAddressConflict) {
1644             final ArpPacket arpProbe = getNextArpPacket();
1645             assertArpProbe(arpProbe);
1646 
1647             if (shouldResponseArpReply) {
1648                 sendArpReply(mClientMac /* dstMac */, ROUTER_MAC_BYTES /* srcMac */,
1649                         INADDR_ANY /* target IP */, CLIENT_ADDR /* sender IP */);
1650             } else {
1651                 sendArpProbe();
1652             }
1653             final DhcpPacket packet = getNextDhcpPacket();
1654             assertTrue(packet instanceof DhcpDeclinePacket);
1655             assertEquals(packet.mServerIdentifier, SERVER_ADDR);
1656             assertEquals(packet.mRequestedIp, CLIENT_ADDR);
1657 
1658             verify(mCb, never()).onProvisioningFailure(any());
1659             assertIpMemoryNeverStoreNetworkAttributes();
1660         } else if (isDhcpIpConflictDetectEnabled) {
1661             int arpPacketCount = 0;
1662             final List<ArpPacket> packetList = new ArrayList<ArpPacket>();
1663             // Total sent ARP packets should be 5 (3 ARP Probes + 2 ARP Announcements)
1664             ArpPacket packet;
1665             while ((packet = getNextArpPacket(TEST_TIMEOUT_MS)) != null) {
1666                 packetList.add(packet);
1667             }
1668             assertEquals(5, packetList.size());
1669             assertArpProbe(packetList.get(0));
1670             assertArpAnnounce(packetList.get(3));
1671         } else {
1672             verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1673             assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime,
1674                     TEST_DEFAULT_MTU);
1675         }
1676     }
1677 
1678     @Test @SignatureRequiredTest(reason = "InterfaceParams.getByName requires CAP_NET_ADMIN")
1679     public void testInterfaceParams() throws Exception {
1680         InterfaceParams params = InterfaceParams.getByName(mIfaceName);
1681         assertNotNull(params);
1682         assertEquals(mIfaceName, params.name);
1683         assertTrue(params.index > 0);
1684         assertNotNull(params.macAddr);
1685         assertTrue(params.hasMacAddress);
1686 
1687         //  Check interface "lo".
1688         params = InterfaceParams.getByName("lo");
1689         assertNotNull(params);
1690         assertEquals("lo", params.name);
1691         assertTrue(params.index > 0);
1692         assertNotNull(params.macAddr);
1693         assertFalse(params.hasMacAddress);
1694     }
1695 
1696     @Test
1697     public void testDhcpInit() throws Exception {
1698         startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
1699                 false /* isPreconnectionEnabled */,
1700                 false /* isDhcpIpConflictDetectEnabled */);
1701         final DhcpPacket packet = getNextDhcpPacket();
1702         assertTrue(packet instanceof DhcpDiscoverPacket);
1703     }
1704 
1705     @Test
1706     public void testHandleSuccessDhcpLease() throws Exception {
1707         final long currentTime = System.currentTimeMillis();
1708         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1709                 false /* shouldReplyRapidCommitAck */,
1710                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1711         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1712         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1713     }
1714 
1715     @Test
1716     public void testHandleFailureDhcpLease() throws Exception {
1717         performDhcpHandshake(false /* isSuccessLease */, TEST_LEASE_DURATION_S,
1718                 false /* shouldReplyRapidCommitAck */,
1719                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1720 
1721         verify(mCb, never()).onProvisioningSuccess(any());
1722         assertIpMemoryNeverStoreNetworkAttributes();
1723     }
1724 
1725     @Test
1726     public void testHandleInfiniteLease() throws Exception {
1727         final long currentTime = System.currentTimeMillis();
1728         performDhcpHandshake(true /* isSuccessLease */, INFINITE_LEASE,
1729                 false /* shouldReplyRapidCommitAck */,
1730                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1731         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1732         assertIpMemoryStoreNetworkAttributes(INFINITE_LEASE, currentTime, TEST_DEFAULT_MTU);
1733     }
1734 
1735     @Test
1736     public void testHandleNoLease() throws Exception {
1737         final long currentTime = System.currentTimeMillis();
1738         performDhcpHandshake(true /* isSuccessLease */, null /* no lease time */,
1739                 false /* shouldReplyRapidCommitAck */,
1740                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1741         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1742         assertIpMemoryStoreNetworkAttributes(null, currentTime, TEST_DEFAULT_MTU);
1743     }
1744 
1745     @Test
1746     public void testHandleRapidCommitOption() throws Exception {
1747         final long currentTime = System.currentTimeMillis();
1748         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1749                 true /* shouldReplyRapidCommitAck */,
1750                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1751         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1752         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1753     }
1754 
1755     @Test
1756     public void testRollbackFromRapidCommitOption() throws Exception {
1757         startIpClientProvisioning(true /* isDhcpRapidCommitEnabled */,
1758                 false /* isPreConnectionEnabled */,
1759                 false /* isDhcpIpConflictDetectEnabled */);
1760 
1761         final List<DhcpPacket> discoverList = new ArrayList<DhcpPacket>();
1762         DhcpPacket packet;
1763         do {
1764             packet = getNextDhcpPacket();
1765             assertTrue(packet instanceof DhcpDiscoverPacket);
1766             discoverList.add(packet);
1767         } while (discoverList.size() < 4);
1768 
1769         // Check the only first 3 DHCPDISCOVERs take rapid commit option.
1770         assertTrue(discoverList.get(0).mRapidCommit);
1771         assertTrue(discoverList.get(1).mRapidCommit);
1772         assertTrue(discoverList.get(2).mRapidCommit);
1773         assertFalse(discoverList.get(3).mRapidCommit);
1774     }
1775 
1776     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1777     public void testDhcpClientStartWithCachedInfiniteLease() throws Exception {
1778         final DhcpPacket packet = getReplyFromDhcpLease(
1779                 new NetworkAttributes.Builder()
1780                     .setAssignedV4Address(CLIENT_ADDR)
1781                     .setAssignedV4AddressExpiry(Long.MAX_VALUE) // lease is always valid
1782                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1783                     .setCluster(TEST_CLUSTER)
1784                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1785                     .build(), false /* timeout */);
1786         assertTrue(packet instanceof DhcpRequestPacket);
1787     }
1788 
1789     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1790     public void testDhcpClientStartWithCachedExpiredLease() throws Exception {
1791         final DhcpPacket packet = getReplyFromDhcpLease(
1792                  new NetworkAttributes.Builder()
1793                     .setAssignedV4Address(CLIENT_ADDR)
1794                     .setAssignedV4AddressExpiry(EXPIRED_LEASE)
1795                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1796                     .setCluster(TEST_CLUSTER)
1797                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1798                     .build(), false /* timeout */);
1799         assertTrue(packet instanceof DhcpDiscoverPacket);
1800     }
1801 
1802     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1803     public void testDhcpClientStartWithNullRetrieveNetworkAttributes() throws Exception {
1804         final DhcpPacket packet = getReplyFromDhcpLease(null /* na */, false /* timeout */);
1805         assertTrue(packet instanceof DhcpDiscoverPacket);
1806     }
1807 
1808     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1809     public void testDhcpClientStartWithTimeoutRetrieveNetworkAttributes() throws Exception {
1810         final DhcpPacket packet = getReplyFromDhcpLease(
1811                 new NetworkAttributes.Builder()
1812                     .setAssignedV4Address(CLIENT_ADDR)
1813                     .setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000)
1814                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1815                     .setCluster(TEST_CLUSTER)
1816                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1817                     .build(), true /* timeout */);
1818         assertTrue(packet instanceof DhcpDiscoverPacket);
1819     }
1820 
1821     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1822     public void testDhcpClientStartWithCachedLeaseWithoutIPAddress() throws Exception {
1823         final DhcpPacket packet = getReplyFromDhcpLease(
1824                 new NetworkAttributes.Builder()
1825                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1826                     .setCluster(TEST_CLUSTER)
1827                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1828                     .build(), false /* timeout */);
1829         assertTrue(packet instanceof DhcpDiscoverPacket);
1830     }
1831 
1832     @Test
1833     public void testDhcpClientRapidCommitEnabled() throws Exception {
1834         startIpClientProvisioning(true /* shouldReplyRapidCommitAck */,
1835                 false /* isPreconnectionEnabled */,
1836                 false /* isDhcpIpConflictDetectEnabled */);
1837         final DhcpPacket packet = getNextDhcpPacket();
1838         assertTrue(packet instanceof DhcpDiscoverPacket);
1839     }
1840 
1841     @Test
1842     public void testDhcpServerInLinkProperties() throws Exception {
1843         performDhcpHandshake();
1844         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1845         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
1846         assertEquals(SERVER_ADDR, captor.getValue().getDhcpServerAddress());
1847     }
1848 
1849     private void createTestNetworkAgentAndRegister(final LinkProperties lp) throws Exception {
1850         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
1851         final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
1852         final NetworkSpecifier testNetworkSpecifier =
1853                 CompatUtil.makeTestNetworkSpecifier(mIfaceName);
1854         final TestableNetworkCallback cb = new TestableNetworkCallback();
1855 
1856         // Requesting a network make sure the NetworkAgent is alive during the whole life cycle of
1857         // requested network.
1858         cm.requestNetwork(new NetworkRequest.Builder()
1859                 .removeCapability(NET_CAPABILITY_TRUSTED)
1860                 .removeCapability(NET_CAPABILITY_INTERNET)
1861                 .addTransportType(TRANSPORT_TEST)
1862                 .setNetworkSpecifier(testNetworkSpecifier)
1863                 .build(), cb);
1864         mNetworkAgent = new TestableNetworkAgent(context, mNetworkAgentThread.getLooper(),
1865                 new NetworkCapabilities.Builder()
1866                         .removeCapability(NET_CAPABILITY_TRUSTED)
1867                         .removeCapability(NET_CAPABILITY_INTERNET)
1868                         .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
1869                         .addCapability(NET_CAPABILITY_NOT_ROAMING)
1870                         .addCapability(NET_CAPABILITY_NOT_VPN)
1871                         .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
1872                         .addTransportType(TRANSPORT_TEST)
1873                         .setNetworkSpecifier(testNetworkSpecifier)
1874                         .build(),
1875                 lp,
1876                 new NetworkAgentConfig.Builder().build());
1877         mNetworkAgent.register();
1878         mNetworkAgent.markConnected();
1879         cb.expectAvailableThenValidatedCallbacks(mNetworkAgent.getNetwork(), TEST_TIMEOUT_MS);
1880     }
1881 
1882     private void assertReceivedDhcpRequestPacketCount() throws Exception {
1883         final List<DhcpPacket> packetList = new ArrayList<>();
1884         DhcpPacket packet;
1885         while ((packet = getNextDhcpPacket(PACKET_TIMEOUT_MS)) != null) {
1886             assertDhcpRequestForReacquire(packet);
1887             packetList.add(packet);
1888         }
1889         assertEquals(1, packetList.size());
1890     }
1891 
1892     private LinkProperties prepareDhcpReacquireTest() throws Exception {
1893         mNetworkAgentThread =
1894                 new HandlerThread(IpClientIntegrationTestCommon.class.getSimpleName());
1895         mNetworkAgentThread.start();
1896 
1897         final long currentTime = System.currentTimeMillis();
1898         setFeatureEnabled(NetworkStackUtils.DHCP_SLOW_RETRANSMISSION_VERSION, true);
1899         performDhcpHandshake(true /* isSuccessLease */,
1900                 TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
1901                 false /* isDhcpIpConflictDetectEnabled */);
1902         final LinkProperties lp =
1903                 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1904         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1905         return lp;
1906     }
1907 
1908     private OnAlarmListener runDhcpRenewTest(final Handler handler, final LinkProperties lp,
1909             final InOrder inOrder) throws Exception {
1910         // Create a NetworkAgent and register it to ConnectivityService with IPv4 LinkProperties,
1911         // then ConnectivityService will call netd API to configure the IPv4 route on the kernel,
1912         // otherwise, unicast DHCPREQUEST cannot be sent out due to no route to host(EHOSTUNREACH).
1913         runAsShell(MANAGE_TEST_NETWORKS, () -> createTestNetworkAgentAndRegister(lp));
1914 
1915         // DHCP client is in BOUND state right now, simulate the renewal via triggering renew alarm
1916         // which should happen at T1. E.g. lease duration is 3600s, T1 = lease_duration * 0.5(1800s)
1917         // T2 = lease_duration * 0.875(3150s).
1918         final OnAlarmListener renewAlarm = expectAlarmSet(inOrder, "RENEW", 1800, handler);
1919         final OnAlarmListener rebindAlarm = expectAlarmSet(inOrder, "REBIND", 3150, handler);
1920 
1921         // Trigger renew alarm and force DHCP client enter RenewingState. Device needs to start
1922         // the ARP resolution for the fake DHCP server IPv4 address before sending the unicast
1923         // DHCPREQUEST out, wait for the unicast ARP request and respond to it with ARP reply,
1924         // otherwise, DHCPREQUEST still cannot be sent out due to that there is no correct ARP
1925         // table for the dest IPv4 address.
1926         handler.post(() -> renewAlarm.onAlarm());
1927         final ArpPacket request = getNextArpPacket();
1928         assertArpRequest(request, SERVER_ADDR);
1929         sendArpReply(request.senderHwAddress.toByteArray() /* dst */, ROUTER_MAC_BYTES /* srcMac */,
1930                 request.senderIp /* target IP */, SERVER_ADDR /* sender IP */);
1931         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
1932 
1933         // Verify there should be only one unicast DHCPREQUESTs to be received per RFC2131.
1934         assertReceivedDhcpRequestPacketCount();
1935 
1936         return rebindAlarm;
1937     }
1938 
1939     @Test @SignatureRequiredTest(reason = "Need to mock the DHCP renew/rebind alarms")
1940     public void testDhcpRenew() throws Exception {
1941         final LinkProperties lp = prepareDhcpReacquireTest();
1942         final InOrder inOrder = inOrder(mAlarm);
1943         runDhcpRenewTest(mDependencies.mDhcpClient.getHandler(), lp, inOrder);
1944     }
1945 
1946     @Test @SignatureRequiredTest(reason = "Need to mock the DHCP renew/rebind alarms")
1947     public void testDhcpRebind() throws Exception {
1948         final LinkProperties lp = prepareDhcpReacquireTest();
1949         final Handler handler = mDependencies.mDhcpClient.getHandler();
1950         final InOrder inOrder = inOrder(mAlarm);
1951         final OnAlarmListener rebindAlarm = runDhcpRenewTest(handler, lp, inOrder);
1952 
1953         // Trigger rebind alarm and forece DHCP client enter RebindingState. DHCP client sends
1954         // broadcast DHCPREQUEST to nearby servers, then check how many DHCPREQUEST packets are
1955         // retransmitted within PACKET_TIMEOUT_MS(5s), there should be only one DHCPREQUEST
1956         // captured per RFC2131.
1957         handler.post(() -> rebindAlarm.onAlarm());
1958         assertReceivedDhcpRequestPacketCount();
1959     }
1960 
1961     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1962     public void testRestoreInitialInterfaceMtu() throws Exception {
1963         doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTestInterface */);
1964     }
1965 
1966     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1967     public void testRestoreInitialInterfaceMtu_WithoutMtuChange() throws Exception {
1968         doRestoreInitialMtuTest(false /* shouldChangeMtu */, false /* shouldRemoveTestInterface */);
1969     }
1970 
1971     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1972     public void testRestoreInitialInterfaceMtu_WithException() throws Exception {
1973         doThrow(new RemoteException("NetdNativeService::interfaceSetMtu")).when(mNetd)
1974                 .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
1975 
1976         doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTestInterface */);
1977         assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU);
1978     }
1979 
1980     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1981     public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStopping() throws Exception {
1982         doRestoreInitialMtuTest(true /* shouldChangeMtu */, true /* shouldRemoveTestInterface */);
1983     }
1984 
1985     @Test
1986     public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStartingProvisioning()
1987             throws Exception {
1988         removeTestInterface(mTapFd);
1989         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1990                 .withoutIpReachabilityMonitor()
1991                 .withoutIPv6()
1992                 .build();
1993 
1994         startIpClientProvisioning(config);
1995         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
1996         verify(mCb, never()).setNeighborDiscoveryOffload(true);
1997     }
1998 
1999     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2000     public void testRestoreInitialInterfaceMtu_stopIpClientAndRestart() throws Exception {
2001         long currentTime = System.currentTimeMillis();
2002 
2003         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2004                 false /* shouldReplyRapidCommitAck */,
2005                 TEST_MIN_MTU, false /* isDhcpIpConflictDetectEnabled */);
2006         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2007         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_MIN_MTU);
2008 
2009         // Pretend that ConnectivityService set the MTU.
2010         mNetd.interfaceSetMtu(mIfaceName, TEST_MIN_MTU);
2011         assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU);
2012 
2013         reset(mCb);
2014         reset(mIpMemoryStore);
2015 
2016         // Stop IpClient and then restart provisioning immediately.
2017         mIpc.stop();
2018         currentTime = System.currentTimeMillis();
2019         // Intend to set mtu option to 0, then verify that won't influence interface mtu restore.
2020         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2021                 false /* shouldReplyRapidCommitAck */,
2022                 0 /* mtu */, false /* isDhcpIpConflictDetectEnabled */);
2023         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2024         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 0 /* mtu */);
2025         assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_DEFAULT_MTU);
2026     }
2027 
2028     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2029     public void testRestoreInitialInterfaceMtu_removeInterfaceAndAddback() throws Exception {
2030         doAnswer(invocation -> {
2031             final LinkProperties lp = invocation.getArgument(0);
2032             assertEquals(lp.getInterfaceName(), mIfaceName);
2033             assertEquals(0, lp.getLinkAddresses().size());
2034             assertEquals(0, lp.getDnsServers().size());
2035 
2036             mDependencies.simulateInterfaceRecover();
2037             return null;
2038         }).when(mCb).onProvisioningFailure(any());
2039 
2040         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2041                 .withoutIpReachabilityMonitor()
2042                 .withoutIPv6()
2043                 .build();
2044 
2045         // Intend to remove the tap interface and force IpClient throw provisioning failure
2046         // due to that interface is not found.
2047         removeTestInterface(mTapFd);
2048         assertNull(InterfaceParams.getByName(mIfaceName));
2049 
2050         startIpClientProvisioning(config);
2051         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
2052 
2053         // Make sure everything queued by this test was processed (e.g. transition to StoppingState
2054         // from ClearingIpAddressState) and tearDown will check if IpClient exits normally or crash.
2055         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
2056     }
2057 
2058     private boolean isIcmpv6PacketOfType(final byte[] packetBytes, int type) {
2059         ByteBuffer packet = ByteBuffer.wrap(packetBytes);
2060         return packet.getShort(ETHER_TYPE_OFFSET) == (short) ETH_P_IPV6
2061                 && packet.get(ETHER_HEADER_LEN + IPV6_PROTOCOL_OFFSET) == (byte) IPPROTO_ICMPV6
2062                 && packet.get(ETHER_HEADER_LEN + IPV6_HEADER_LEN) == (byte) type;
2063     }
2064 
2065     private boolean isRouterSolicitation(final byte[] packetBytes) {
2066         return isIcmpv6PacketOfType(packetBytes, ICMPV6_ROUTER_SOLICITATION);
2067     }
2068 
2069     private boolean isNeighborAdvertisement(final byte[] packetBytes) {
2070         return isIcmpv6PacketOfType(packetBytes, ICMPV6_NEIGHBOR_ADVERTISEMENT);
2071     }
2072 
2073     private boolean isNeighborSolicitation(final byte[] packetBytes) {
2074         return isIcmpv6PacketOfType(packetBytes, ICMPV6_NEIGHBOR_SOLICITATION);
2075     }
2076 
2077     private NeighborAdvertisement getNextNeighborAdvertisement() throws ParseException {
2078         final byte[] packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS,
2079                 this::isNeighborAdvertisement);
2080         if (packet == null) return null;
2081 
2082         final NeighborAdvertisement na = parseNeighborAdvertisementOrNull(packet);
2083         assertNotNull("Invalid neighbour advertisement received", na);
2084         return na;
2085     }
2086 
2087     private NeighborSolicitation getNextNeighborSolicitation() throws ParseException {
2088         final byte[] packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS,
2089                 this::isNeighborSolicitation);
2090         if (packet == null) return null;
2091 
2092         final NeighborSolicitation ns = parseNeighborSolicitationOrNull(packet);
2093         assertNotNull("Invalid neighbour solicitation received", ns);
2094         return ns;
2095     }
2096 
2097     private void waitForRouterSolicitation() throws ParseException {
2098         assertNotNull("No router solicitation received on interface within timeout",
2099                 mPacketReader.popPacket(PACKET_TIMEOUT_MS, this::isRouterSolicitation));
2100     }
2101 
2102     private void sendRouterAdvertisement(boolean waitForRs, short lifetime, int valid,
2103             int preferred) throws Exception {
2104         final ByteBuffer pio = buildPioOption(valid, preferred, "2001:db8:1::/64");
2105         final ByteBuffer rdnss = buildRdnssOption(3600, IPV6_OFF_LINK_DNS_SERVER);
2106         sendRouterAdvertisement(waitForRs, lifetime, pio, rdnss);
2107     }
2108 
2109     private void sendRouterAdvertisement(boolean waitForRs, short lifetime,
2110             ByteBuffer... options) throws Exception {
2111         final ByteBuffer ra = buildRaPacket(lifetime, options);
2112         if (waitForRs) {
2113             waitForRouterSolicitation();
2114         }
2115         mPacketReader.sendResponse(ra);
2116     }
2117 
2118     private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception {
2119         sendRouterAdvertisement(waitForRs, (short) 1800 /* lifetime */, 3600 /* valid */,
2120                 1800 /* preferred */);
2121     }
2122 
2123     private void sendRouterAdvertisementWithZeroRouterLifetime() throws Exception {
2124         sendRouterAdvertisement(false /* waitForRs */, (short) 0 /* lifetime */, 3600 /* valid */,
2125                 1800 /* preferred */);
2126     }
2127 
2128     // TODO: move this and the following method to a common location and use them in ApfTest.
2129     private static ByteBuffer buildPioOption(int valid, int preferred, String prefixString)
2130             throws Exception {
2131         return PrefixInformationOption.build(new IpPrefix(prefixString),
2132                 (byte) (PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS), valid, preferred);
2133     }
2134 
2135     private static ByteBuffer buildRdnssOption(int lifetime, String... servers) throws Exception {
2136         return RdnssOption.build(lifetime, servers);
2137     }
2138 
2139     private static ByteBuffer buildSllaOption() throws Exception {
2140         return LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, ROUTER_MAC);
2141     }
2142 
2143     private static ByteBuffer buildRaPacket(short lifetime, ByteBuffer... options)
2144             throws Exception {
2145         final MacAddress dstMac =
2146                 NetworkStackUtils.ipv6MulticastToEthernetMulticast(IPV6_ADDR_ALL_ROUTERS_MULTICAST);
2147         return Ipv6Utils.buildRaPacket(ROUTER_MAC /* srcMac */, dstMac,
2148                 ROUTER_LINK_LOCAL /* srcIp */, IPV6_ADDR_ALL_NODES_MULTICAST /* dstIp */,
2149                 (byte) 0 /* M=0, O=0 */, lifetime, 0 /* Reachable time, unspecified */,
2150                 100 /* Retrans time 100ms */, options);
2151     }
2152 
2153     private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception {
2154         return buildRaPacket((short) 1800, options);
2155     }
2156 
2157     private void disableIpv6ProvisioningDelays() throws Exception {
2158         // Speed up the test by disabling DAD and removing router_solicitation_delay.
2159         // We don't need to restore the default value because the interface is removed in tearDown.
2160         // TODO: speed up further by not waiting for RS but keying off first IPv6 packet.
2161         mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "router_solicitation_delay", "0");
2162         mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits", "0");
2163     }
2164 
2165     private void assertHasAddressThat(String msg, LinkProperties lp,
2166             Predicate<LinkAddress> condition) {
2167         for (LinkAddress addr : lp.getLinkAddresses()) {
2168             if (condition.test(addr)) {
2169                 return;
2170             }
2171         }
2172         fail(msg + " not found in: " + lp);
2173     }
2174 
2175     private boolean hasFlag(LinkAddress addr, int flag) {
2176         return (addr.getFlags() & flag) == flag;
2177     }
2178 
2179     private boolean isPrivacyAddress(LinkAddress addr) {
2180         return addr.isGlobalPreferred() && hasFlag(addr, IFA_F_TEMPORARY);
2181     }
2182 
2183     private boolean isStablePrivacyAddress(LinkAddress addr) {
2184         return addr.isGlobalPreferred() && hasFlag(addr, IFA_F_STABLE_PRIVACY);
2185     }
2186 
2187     private LinkProperties doIpv6OnlyProvisioning() throws Exception {
2188         final InOrder inOrder = inOrder(mCb);
2189         final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64");
2190         final ByteBuffer rdnss = buildRdnssOption(3600, IPV6_OFF_LINK_DNS_SERVER);
2191         final ByteBuffer slla = buildSllaOption();
2192         final ByteBuffer ra = buildRaPacket(pio, rdnss, slla);
2193 
2194         return doIpv6OnlyProvisioning(inOrder, ra);
2195     }
2196 
2197     private LinkProperties doIpv6OnlyProvisioning(InOrder inOrder, ByteBuffer ra) throws Exception {
2198         waitForRouterSolicitation();
2199         mPacketReader.sendResponse(ra);
2200 
2201         // The lambda below needs to write a LinkProperties to a local variable, but lambdas cannot
2202         // write to non-final local variables. So declare a final variable to write to.
2203         final AtomicReference<LinkProperties> lpRef = new AtomicReference<>();
2204 
2205         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
2206         verifyWithTimeout(inOrder, mCb).onProvisioningSuccess(captor.capture());
2207         lpRef.set(captor.getValue());
2208 
2209         // Sometimes provisioning completes as soon as the link-local and the stable address appear,
2210         // before the privacy address appears. If so, wait here for the LinkProperties update that
2211         // contains all three address. Otherwise, future calls to verify() might get confused.
2212         if (captor.getValue().getLinkAddresses().size() == 2) {
2213             verifyWithTimeout(inOrder, mCb).onLinkPropertiesChange(argThat(lp -> {
2214                 lpRef.set(lp);
2215                 return lp.getLinkAddresses().size() == 3;
2216             }));
2217         }
2218 
2219         LinkProperties lp = lpRef.get();
2220         assertEquals("Should have 3 IPv6 addresses after provisioning: " + lp,
2221                 3, lp.getLinkAddresses().size());
2222         assertHasAddressThat("link-local address", lp, x -> x.getAddress().isLinkLocalAddress());
2223         assertHasAddressThat("privacy address", lp, this::isPrivacyAddress);
2224         assertHasAddressThat("stable privacy address", lp, this::isStablePrivacyAddress);
2225 
2226         return lp;
2227     }
2228 
2229     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2230     public void testRaRdnss() throws Exception {
2231         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2232                 .withoutIpReachabilityMonitor()
2233                 .withoutIPv4()
2234                 .build();
2235         startIpClientProvisioning(config);
2236 
2237         InOrder inOrder = inOrder(mCb);
2238         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
2239 
2240         final String dnsServer = "2001:4860:4860::64";
2241         final String lowlifeDnsServer = "2001:4860:4860::6464";
2242 
2243         final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64");
2244         ByteBuffer rdnss1 = buildRdnssOption(60, lowlifeDnsServer);
2245         ByteBuffer rdnss2 = buildRdnssOption(600, dnsServer);
2246         ByteBuffer ra = buildRaPacket(pio, rdnss1, rdnss2);
2247 
2248         LinkProperties lp = doIpv6OnlyProvisioning(inOrder, ra);
2249 
2250         // Expect that DNS servers with lifetimes below CONFIG_ACCEPT_RA_MIN_LFT are not accepted.
2251         assertNotNull(lp);
2252         assertEquals(1, lp.getDnsServers().size());
2253         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
2254 
2255         // If the RDNSS lifetime is above the minimum, the DNS server is accepted.
2256         rdnss1 = buildRdnssOption(67, lowlifeDnsServer);
2257         ra = buildRaPacket(pio, rdnss1, rdnss2);
2258         mPacketReader.sendResponse(ra);
2259         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(captor.capture());
2260         lp = captor.getValue();
2261         assertNotNull(lp);
2262         assertEquals(2, lp.getDnsServers().size());
2263         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
2264         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(lowlifeDnsServer)));
2265 
2266         // Expect that setting RDNSS lifetime of 0 causes loss of provisioning.
2267         rdnss1 = buildRdnssOption(0, dnsServer);
2268         rdnss2 = buildRdnssOption(0, lowlifeDnsServer);
2269         ra = buildRaPacket(pio, rdnss1, rdnss2);
2270         mPacketReader.sendResponse(ra);
2271 
2272         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
2273         lp = captor.getValue();
2274         assertNotNull(lp);
2275         assertEquals(0, lp.getDnsServers().size());
2276         reset(mCb);
2277     }
2278 
2279     private void runRaRdnssIpv6LinkLocalDnsTest() throws Exception {
2280         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2281                 .withoutIpReachabilityMonitor()
2282                 .withoutIPv4()
2283                 .build();
2284         startIpClientProvisioning(config);
2285 
2286         final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64");
2287         // put an IPv6 link-local DNS server
2288         final ByteBuffer rdnss = buildRdnssOption(600, ROUTER_LINK_LOCAL.getHostAddress());
2289         // put SLLA option to avoid address resolution for "fe80::1"
2290         final ByteBuffer slla = buildSllaOption();
2291         final ByteBuffer ra = buildRaPacket(pio, rdnss, slla);
2292 
2293         waitForRouterSolicitation();
2294         mPacketReader.sendResponse(ra);
2295     }
2296 
2297     @Test
2298     public void testRaRdnss_Ipv6LinkLocalDns() throws Exception {
2299         runRaRdnssIpv6LinkLocalDnsTest();
2300         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
2301         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
2302         final LinkProperties lp = captor.getValue();
2303         assertNotNull(lp);
2304         assertEquals(1, lp.getDnsServers().size());
2305         assertEquals(ROUTER_LINK_LOCAL, (Inet6Address) lp.getDnsServers().get(0));
2306         assertTrue(lp.isIpv6Provisioned());
2307     }
2308 
2309     private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception {
2310         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(
2311                 argThat(lp -> Objects.equals(expected, lp.getNat64Prefix())));
2312 
2313     }
2314 
2315     private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception {
2316         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(argThat(
2317                 lp -> !Objects.equals(unchanged, lp.getNat64Prefix())));
2318 
2319     }
2320 
2321     @Test
2322     @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2323     public void testPref64Option() throws Exception {
2324         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2325                 .withoutIpReachabilityMonitor()
2326                 .withoutIPv4()
2327                 .build();
2328         startIpClientProvisioning(config);
2329 
2330         final IpPrefix prefix = new IpPrefix("64:ff9b::/96");
2331         final IpPrefix otherPrefix = new IpPrefix("2001:db8:64::/96");
2332 
2333         final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64");
2334         ByteBuffer rdnss = buildRdnssOption(600, IPV6_OFF_LINK_DNS_SERVER);
2335         ByteBuffer pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
2336         ByteBuffer ra = buildRaPacket(pio, rdnss, pref64);
2337 
2338         // The NAT64 prefix might be detected before or after provisioning success.
2339         // Don't test order between these two events.
2340         LinkProperties lp = doIpv6OnlyProvisioning(null /*inOrder*/, ra);
2341         expectAlarmSet(null /*inOrder*/, "PREF64", 600);
2342 
2343         // From now on expect events in order.
2344         InOrder inOrder = inOrder(mCb, mAlarm);
2345         if (lp.getNat64Prefix() != null) {
2346             assertEquals(prefix, lp.getNat64Prefix());
2347         } else {
2348             expectNat64PrefixUpdate(inOrder, prefix);
2349         }
2350 
2351         // Increase the lifetime and expect the prefix not to change.
2352         pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer();
2353         ra = buildRaPacket(pio, rdnss, pref64);
2354         mPacketReader.sendResponse(ra);
2355         OnAlarmListener pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1800);
2356         expectNoNat64PrefixUpdate(inOrder, prefix);
2357         reset(mCb, mAlarm);
2358 
2359         // Reduce the lifetime and expect to reschedule expiry.
2360         pref64 = new StructNdOptPref64(prefix, 1500).toByteBuffer();
2361         ra = buildRaPacket(pio, rdnss, pref64);
2362         mPacketReader.sendResponse(ra);
2363         pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1496);
2364         expectNoNat64PrefixUpdate(inOrder, prefix);
2365         reset(mCb, mAlarm);
2366 
2367         // Withdraw the prefix and expect it to be set to null.
2368         pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer();
2369         ra = buildRaPacket(pio, rdnss, pref64);
2370         mPacketReader.sendResponse(ra);
2371         expectAlarmCancelled(inOrder, pref64Alarm);
2372         expectNat64PrefixUpdate(inOrder, null);
2373         reset(mCb, mAlarm);
2374 
2375         // Re-announce the prefix.
2376         pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
2377         ra = buildRaPacket(pio, rdnss, pref64);
2378         mPacketReader.sendResponse(ra);
2379         expectAlarmSet(inOrder, "PREF64", 600);
2380         expectNat64PrefixUpdate(inOrder, prefix);
2381         reset(mCb, mAlarm);
2382 
2383         // Announce two prefixes. Don't expect any update because if there is already a NAT64
2384         // prefix, any new prefix is ignored.
2385         ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 1200).toByteBuffer();
2386         ra = buildRaPacket(pio, rdnss, pref64, otherPref64);
2387         mPacketReader.sendResponse(ra);
2388         expectAlarmSet(inOrder, "PREF64", 600);
2389         expectNoNat64PrefixUpdate(inOrder, prefix);
2390         reset(mCb, mAlarm);
2391 
2392         // Withdraw the old prefix and continue to announce the new one. Expect a prefix change.
2393         pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer();
2394         ra = buildRaPacket(pio, rdnss, pref64, otherPref64);
2395         mPacketReader.sendResponse(ra);
2396         expectAlarmCancelled(inOrder, pref64Alarm);
2397         // Need a different OnAlarmListener local variable because posting it to the handler in the
2398         // lambda below requires it to be final.
2399         final OnAlarmListener lastAlarm = expectAlarmSet(inOrder, "PREF64", 1200);
2400         expectNat64PrefixUpdate(inOrder, otherPrefix);
2401         reset(mCb, mAlarm);
2402 
2403         // Simulate prefix expiry.
2404         mIpc.getHandler().post(() -> lastAlarm.onAlarm());
2405         expectAlarmCancelled(inOrder, pref64Alarm);
2406         expectNat64PrefixUpdate(inOrder, null);
2407 
2408         // Announce a non-/96 prefix and expect it to be ignored.
2409         IpPrefix invalidPrefix = new IpPrefix("64:ff9b::/64");
2410         pref64 = new StructNdOptPref64(invalidPrefix, 1200).toByteBuffer();
2411         ra = buildRaPacket(pio, rdnss, pref64);
2412         mPacketReader.sendResponse(ra);
2413         expectNoNat64PrefixUpdate(inOrder, invalidPrefix);
2414 
2415         // Re-announce the prefix.
2416         pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
2417         ra = buildRaPacket(pio, rdnss, pref64);
2418         mPacketReader.sendResponse(ra);
2419         final OnAlarmListener clearAlarm = expectAlarmSet(inOrder, "PREF64", 600);
2420         expectNat64PrefixUpdate(inOrder, prefix);
2421         reset(mCb, mAlarm);
2422 
2423         // Check that the alarm is cancelled when IpClient is stopped.
2424         mIpc.stop();
2425         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
2426         expectAlarmCancelled(inOrder, clearAlarm);
2427         expectNat64PrefixUpdate(inOrder, null);
2428 
2429         // Check that even if the alarm was already in the message queue while it was cancelled, it
2430         // is safely ignored.
2431         mIpc.getHandler().post(() -> clearAlarm.onAlarm());
2432         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
2433     }
2434 
2435     private void addIpAddressAndWaitForIt(final String iface) throws Exception {
2436         final String addr1 = "192.0.2.99";
2437         final String addr2 = "192.0.2.3";
2438         final int prefixLength = 26;
2439 
2440         // IpClient gets IP addresses directly from netlink instead of from netd, just
2441         // add the addresses directly and wait to see if IpClient has seen the address.
2442         mNetd.interfaceAddAddress(iface, addr1, prefixLength);
2443         mNetd.interfaceAddAddress(iface, addr2, prefixLength);
2444 
2445         // Wait for IpClient to process the addition of the address.
2446         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
2447     }
2448 
2449     private void doIPv4OnlyProvisioningAndExitWithLeftAddress() throws Exception {
2450         final long currentTime = System.currentTimeMillis();
2451         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2452                 false /* shouldReplyRapidCommitAck */,
2453                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
2454         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2455         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2456 
2457         // Stop IpClient and expect a final LinkProperties callback with an empty LP.
2458         mIIpClient.stop();
2459         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(
2460                 x -> x.getAddresses().size() == 0
2461                         && x.getRoutes().size() == 0
2462                         && x.getDnsServers().size() == 0));
2463         reset(mCb);
2464 
2465         // Pretend that something else (e.g., Tethering) used the interface and left an IP address
2466         // configured on it. When IpClient starts, it must clear this address before proceeding.
2467         // The address must be noticed before startProvisioning is called, or IpClient will
2468         // immediately declare provisioning success due to the presence of an IPv4 address.
2469         // The address must be IPv4 because IpClient clears IPv6 addresses on startup.
2470         addIpAddressAndWaitForIt(mIfaceName);
2471     }
2472 
2473     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2474     public void testIpClientClearingIpAddressState() throws Exception {
2475         doIPv4OnlyProvisioningAndExitWithLeftAddress();
2476 
2477         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2478                 .withoutIpReachabilityMonitor()
2479                 .build();
2480         startIpClientProvisioning(config);
2481 
2482         sendBasicRouterAdvertisement(true /*waitForRs*/);
2483 
2484         // Check that the IPv4 addresses configured earlier are not in LinkProperties...
2485         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
2486         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
2487         assertFalse(captor.getValue().hasIpv4Address());
2488 
2489         // ... or configured on the interface.
2490         InterfaceConfigurationParcel cfg = mNetd.interfaceGetCfg(mIfaceName);
2491         assertEquals("0.0.0.0", cfg.ipv4Addr);
2492     }
2493 
2494     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2495     public void testIpClientClearingIpAddressState_enablePreconnection() throws Exception {
2496         doIPv4OnlyProvisioningAndExitWithLeftAddress();
2497 
2498         // Enter ClearingIpAddressesState to clear the remaining IPv4 addresses and transition to
2499         // PreconnectionState instead of RunningState.
2500         startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
2501                 true /* isDhcpPreConnectionEnabled */,
2502                 false /* isDhcpIpConflictDetectEnabled */);
2503         assertDiscoverPacketOnPreconnectionStart();
2504 
2505         // Force to enter RunningState.
2506         mIpc.notifyPreconnectionComplete(false /* abort */);
2507         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
2508     }
2509 
2510     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2511     public void testDhcpClientPreconnection_success() throws Exception {
2512         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
2513                 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
2514                 false /* timeoutBeforePreconnectionComplete */);
2515     }
2516 
2517     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2518     public void testDhcpClientPreconnection_SuccessWithoutRapidCommit() throws Exception {
2519         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
2520                 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
2521                 false /* timeoutBeforePreconnectionComplete */);
2522     }
2523 
2524     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2525     public void testDhcpClientPreconnection_Abort() throws Exception {
2526         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
2527                 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
2528                 false /* timeoutBeforePreconnectionComplete */);
2529     }
2530 
2531     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2532     public void testDhcpClientPreconnection_AbortWithoutRapiCommit() throws Exception {
2533         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
2534                 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
2535                 false /* timeoutBeforePreconnectionComplete */);
2536     }
2537 
2538     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2539     public void testDhcpClientPreconnection_TimeoutBeforeAbort() throws Exception {
2540         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
2541                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
2542                 true /* timeoutBeforePreconnectionComplete */);
2543     }
2544 
2545     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2546     public void testDhcpClientPreconnection_TimeoutBeforeAbortWithoutRapidCommit()
2547             throws Exception {
2548         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
2549                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
2550                 true /* timeoutBeforePreconnectionComplete */);
2551     }
2552 
2553     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2554     public void testDhcpClientPreconnection_TimeoutafterAbort() throws Exception {
2555         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
2556                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
2557                 false /* timeoutBeforePreconnectionComplete */);
2558     }
2559 
2560     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2561     public void testDhcpClientPreconnection_TimeoutAfterAbortWithoutRapidCommit() throws Exception {
2562         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
2563                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
2564                 false /* timeoutBeforePreconnectionComplete */);
2565     }
2566 
2567     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2568     public void testDhcpClientPreconnection_TimeoutBeforeSuccess() throws Exception {
2569         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
2570                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
2571                 true /* timeoutBeforePreconnectionComplete */);
2572     }
2573 
2574     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2575     public void testDhcpClientPreconnection_TimeoutBeforeSuccessWithoutRapidCommit()
2576             throws Exception {
2577         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
2578                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
2579                 true /* timeoutBeforePreconnectionComplete */);
2580     }
2581 
2582     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2583     public void testDhcpClientPreconnection_TimeoutAfterSuccess() throws Exception {
2584         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
2585                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
2586                 false /* timeoutBeforePreconnectionComplete */);
2587     }
2588 
2589     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2590     public void testDhcpClientPreconnection_TimeoutAfterSuccessWithoutRapidCommit()
2591             throws Exception {
2592         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
2593                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
2594                 false /* timeoutBeforePreconnectionComplete */);
2595     }
2596 
2597     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2598     public void testDhcpClientPreconnection_WithoutLayer2InfoWhenStartingProv() throws Exception {
2599         // For FILS connection, current bssid (also l2key and cluster) is still null when
2600         // starting provisioning since the L2 link hasn't been established yet. Ensure that
2601         // IpClient won't crash even if initializing an Layer2Info class with null members.
2602         ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
2603                 .withoutIpReachabilityMonitor()
2604                 .withoutIPv6()
2605                 .withPreconnection()
2606                 .withLayer2Information(new Layer2Information(null /* l2key */, null /* cluster */,
2607                         null /* bssid */));
2608 
2609         startIpClientProvisioning(prov.build());
2610         assertDiscoverPacketOnPreconnectionStart();
2611         verify(mCb).setNeighborDiscoveryOffload(true);
2612 
2613         // Force IpClient transition to RunningState from PreconnectionState.
2614         mIIpClient.notifyPreconnectionComplete(false /* success */);
2615         HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
2616         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true);
2617     }
2618 
2619     @Test
2620     @SignatureRequiredTest(reason = "needs mocked alarm and access to IpClient handler thread")
2621     public void testDhcpClientPreconnection_DelayedAbortAndTransitToStoppedState()
2622             throws Exception {
2623         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2624                 .withoutIpReachabilityMonitor()
2625                 .withPreconnection()
2626                 .build();
2627         setDhcpFeatures(false /* shouldReplyRapidCommitAck */,
2628                 false /* isDhcpIpConflictDetectEnabled */);
2629         startIpClientProvisioning(config);
2630         assertDiscoverPacketOnPreconnectionStart();
2631 
2632         // IpClient is in the PreconnectingState, simulate provisioning timeout event
2633         // and force IpClient state machine transit to StoppingState.
2634         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
2635         final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 18,
2636                 mIpc.getHandler());
2637         mIpc.getHandler().post(() -> alarm.onAlarm());
2638 
2639         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
2640         final LinkProperties lp = captor.getValue();
2641         assertNotNull(lp);
2642         assertEquals(mIfaceName, lp.getInterfaceName());
2643         assertEquals(0, lp.getLinkAddresses().size());
2644         assertEquals(0, lp.getRoutes().size());
2645         assertEquals(0, lp.getMtu());
2646         assertEquals(0, lp.getDnsServers().size());
2647 
2648         // Send preconnection abort message, but IpClient should ignore it at this moment and
2649         // transit to StoppedState finally.
2650         mIpc.notifyPreconnectionComplete(false /* abort */);
2651         mIpc.stop();
2652         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
2653 
2654         reset(mCb);
2655 
2656         // Start provisioning again to verify IpClient can process CMD_START correctly at
2657         // StoppedState.
2658         startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
2659                 false /* isPreConnectionEnabled */,
2660                 false /* isDhcpIpConflictDetectEnabled */);
2661         final DhcpPacket discover = getNextDhcpPacket();
2662         assertTrue(discover instanceof DhcpDiscoverPacket);
2663     }
2664 
2665     @Test
2666     public void testDhcpDecline_conflictByArpReply() throws Exception {
2667         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
2668                 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2669                 true /* shouldResponseArpReply */);
2670     }
2671 
2672     @Test
2673     public void testDhcpDecline_conflictByArpProbe() throws Exception {
2674         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
2675                 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2676                 false /* shouldResponseArpReply */);
2677     }
2678 
2679     @Test
2680     public void testDhcpDecline_EnableFlagWithoutIpConflict() throws Exception {
2681         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
2682                 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2683                 false /* shouldResponseArpReply */);
2684     }
2685 
2686     @Test
2687     public void testDhcpDecline_WithoutIpConflict() throws Exception {
2688         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
2689                 false /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */,
2690                 false /* shouldResponseArpReply */);
2691     }
2692 
2693     @Test
2694     public void testDhcpDecline_WithRapidCommitWithoutIpConflict() throws Exception {
2695         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
2696                 true /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */,
2697                 false /* shouldResponseArpReply */);
2698     }
2699 
2700     @Test
2701     public void testDhcpDecline_WithRapidCommitConflictByArpReply() throws Exception {
2702         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
2703                 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2704                 true /* shouldResponseArpReply */);
2705     }
2706 
2707     @Test
2708     public void testDhcpDecline_WithRapidCommitConflictByArpProbe() throws Exception {
2709         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
2710                 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2711                 false /* shouldResponseArpReply */);
2712     }
2713 
2714     @Test
2715     public void testDhcpDecline_EnableFlagWithRapidCommitWithoutIpConflict() throws Exception {
2716         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
2717                 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2718                 false /* shouldResponseArpReply */);
2719     }
2720 
2721     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2722     public void testHostname_enableConfig() throws Exception {
2723         mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */,
2724                 TEST_HOST_NAME);
2725 
2726         final long currentTime = System.currentTimeMillis();
2727         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
2728                 TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
2729                 false /* isDhcpIpConflictDetectEnabled */);
2730 
2731         assertEquals(2, sentPackets.size());
2732         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2733         assertHostname(true, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
2734         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2735     }
2736 
2737     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2738     public void testHostname_disableConfig() throws Exception {
2739         mDependencies.setHostnameConfiguration(false /* isHostnameConfigurationEnabled */,
2740                 TEST_HOST_NAME);
2741 
2742         final long currentTime = System.currentTimeMillis();
2743         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
2744                 TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
2745                 false /* isDhcpIpConflictDetectEnabled */);
2746 
2747         assertEquals(2, sentPackets.size());
2748         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2749         assertHostname(false, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
2750         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2751     }
2752 
2753     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2754     public void testHostname_enableConfigWithNullHostname() throws Exception {
2755         mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */,
2756                 null /* hostname */);
2757 
2758         final long currentTime = System.currentTimeMillis();
2759         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
2760                 TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
2761                 false /* isDhcpIpConflictDetectEnabled */);
2762 
2763         assertEquals(2, sentPackets.size());
2764         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2765         assertHostname(true, null /* hostname */, null /* hostnameAfterTransliteration */,
2766                 sentPackets);
2767         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2768     }
2769 
2770     private LinkProperties runDhcpClientCaptivePortalApiTest(boolean featureEnabled,
2771             boolean serverSendsOption) throws Exception {
2772         startIpClientProvisioning(false /* shouldReplyRapidCommitAck */,
2773                 false /* isPreConnectionEnabled */,
2774                 false /* isDhcpIpConflictDetectEnabled */);
2775         final DhcpPacket discover = getNextDhcpPacket();
2776         assertTrue(discover instanceof DhcpDiscoverPacket);
2777         assertEquals(featureEnabled, discover.hasRequestedParam(DhcpPacket.DHCP_CAPTIVE_PORTAL));
2778 
2779         // Send Offer and handle Request -> Ack
2780         final String serverSentUrl = serverSendsOption ? TEST_CAPTIVE_PORTAL_URL : null;
2781         mPacketReader.sendResponse(buildDhcpOfferPacket(discover, CLIENT_ADDR,
2782                 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, serverSentUrl));
2783         final int testMtu = 1345;
2784         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2785                 false /* shouldReplyRapidCommitAck */, testMtu, serverSentUrl);
2786 
2787         final Uri expectedUrl = featureEnabled && serverSendsOption
2788                 ? Uri.parse(TEST_CAPTIVE_PORTAL_URL) : null;
2789         // LinkProperties will be updated multiple times. Wait for it to contain DHCP-obtained info,
2790         // such as MTU.
2791         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
2792         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(
2793                 argThat(lp -> lp.getMtu() == testMtu));
2794 
2795         // Ensure that the URL was set as expected in the callbacks.
2796         verify(mCb, atLeastOnce()).onLinkPropertiesChange(captor.capture());
2797         final LinkProperties expectedLp = captor.getAllValues().stream().findFirst().get();
2798         assertNotNull(expectedLp);
2799         assertEquals(expectedUrl, expectedLp.getCaptivePortalApiUrl());
2800         return expectedLp;
2801     }
2802 
2803     @Test
2804     public void testDhcpClientCaptivePortalApiEnabled() throws Exception {
2805         // Only run the test on platforms / builds where the API is enabled
2806         assumeTrue(CaptivePortalDataShimImpl.isSupported());
2807         runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, true /* serverSendsOption */);
2808     }
2809 
2810     @Test
2811     public void testDhcpClientCaptivePortalApiEnabled_NoUrl() throws Exception {
2812         // Only run the test on platforms / builds where the API is enabled
2813         assumeTrue(CaptivePortalDataShimImpl.isSupported());
2814         runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, false /* serverSendsOption */);
2815     }
2816 
2817     @Test
2818     public void testDhcpClientCaptivePortalApiEnabled_ParcelSensitiveFields() throws Exception {
2819         // Only run the test on platforms / builds where the API is enabled
2820         assumeTrue(CaptivePortalDataShimImpl.isSupported());
2821         LinkProperties lp = runDhcpClientCaptivePortalApiTest(true /* featureEnabled */,
2822                 true /* serverSendsOption */);
2823 
2824         // Integration test process runs in the same process with network stack module, there
2825         // won't be any IPC call happened on IpClientCallbacks, manually run parcelingRoundTrip
2826         // to parcel and unparcel the LinkProperties to simulate what happens during the binder
2827         // call. In this case lp should contain the senstive data but mParcelSensitiveFields is
2828         // false after round trip.
2829         if (useNetworkStackSignature()) {
2830             lp = parcelingRoundTrip(lp);
2831         }
2832         final Uri expectedUrl = Uri.parse(TEST_CAPTIVE_PORTAL_URL);
2833         assertEquals(expectedUrl, lp.getCaptivePortalApiUrl());
2834 
2835         // Parcel and unparcel the captured LinkProperties, mParcelSensitiveFields is false,
2836         // CaptivePortalApiUrl should be null after parceling round trip.
2837         final LinkProperties unparceled = parcelingRoundTrip(lp);
2838         assertNull(unparceled.getCaptivePortalApiUrl());
2839     }
2840 
2841     @Test
2842     public void testDhcpClientCaptivePortalApiDisabled() throws Exception {
2843         // Only run the test on platforms / builds where the API is disabled
2844         assumeFalse(CaptivePortalDataShimImpl.isSupported());
2845         runDhcpClientCaptivePortalApiTest(false /* featureEnabled */, true /* serverSendsOption */);
2846     }
2847 
2848     private ScanResultInfo makeScanResultInfo(final int id, final String ssid,
2849             final String bssid, final byte[] oui, final byte type, final byte[] data) {
2850         final ByteBuffer payload = ByteBuffer.allocate(4 + data.length);
2851         payload.put(oui);
2852         payload.put(type);
2853         payload.put(data);
2854         payload.flip();
2855         final ScanResultInfo.InformationElement ie =
2856                 new ScanResultInfo.InformationElement(id /* IE id */, payload);
2857         return new ScanResultInfo(ssid, bssid, Collections.singletonList(ie));
2858     }
2859 
2860     private ScanResultInfo makeScanResultInfo(final int id, final byte[] oui, final byte type) {
2861         byte[] data = new byte[10];
2862         new Random().nextBytes(data);
2863         return makeScanResultInfo(id, TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID, oui, type, data);
2864     }
2865 
2866     private ScanResultInfo makeScanResultInfo(final String ssid, final String bssid) {
2867         byte[] data = new byte[10];
2868         new Random().nextBytes(data);
2869         return makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, ssid, bssid, TEST_AP_OUI,
2870                 (byte) 0x06, data);
2871     }
2872 
2873     private void doUpstreamHotspotDetectionTest(final int id, final String displayName,
2874             final String ssid, final byte[] oui, final byte type, final byte[] data,
2875             final boolean expectMetered) throws Exception {
2876         final ScanResultInfo info = makeScanResultInfo(id, ssid, TEST_DEFAULT_BSSID, oui, type,
2877                 data);
2878         final long currentTime = System.currentTimeMillis();
2879         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
2880                 TEST_LEASE_DURATION_S, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
2881                 false /* isDhcpIpConflictDetectEnabled */,
2882                 null /* captivePortalApiUrl */, displayName, info /* scanResultInfo */,
2883                 null /* layer2Info */);
2884         assertEquals(2, sentPackets.size());
2885         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2886 
2887         ArgumentCaptor<DhcpResultsParcelable> captor =
2888                 ArgumentCaptor.forClass(DhcpResultsParcelable.class);
2889         verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture());
2890         final DhcpResultsParcelable lease = captor.getValue();
2891         assertNotNull(lease);
2892         assertEquals(CLIENT_ADDR, lease.baseConfiguration.getIpAddress().getAddress());
2893         assertEquals(SERVER_ADDR, lease.baseConfiguration.getGateway());
2894         assertEquals(1, lease.baseConfiguration.getDnsServers().size());
2895         assertTrue(lease.baseConfiguration.getDnsServers().contains(SERVER_ADDR));
2896         assertEquals(SERVER_ADDR, InetAddresses.parseNumericAddress(lease.serverAddress));
2897         assertEquals(TEST_DEFAULT_MTU, lease.mtu);
2898 
2899         if (expectMetered) {
2900             assertEquals(lease.vendorInfo, DhcpPacket.VENDOR_INFO_ANDROID_METERED);
2901         } else {
2902             assertNull(lease.vendorInfo);
2903         }
2904 
2905         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2906     }
2907 
2908     @Test
2909     public void testUpstreamHotspotDetection() throws Exception {
2910         byte[] data = new byte[10];
2911         new Random().nextBytes(data);
2912         doUpstreamHotspotDetectionTest(TEST_VENDOR_SPECIFIC_IE_ID, "\"ssid\"", "ssid",
2913                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2914                 true /* expectMetered */);
2915     }
2916 
2917     @Test
2918     public void testUpstreamHotspotDetection_incorrectIeId() throws Exception {
2919         byte[] data = new byte[10];
2920         new Random().nextBytes(data);
2921         doUpstreamHotspotDetectionTest(0xdc, "\"ssid\"", "ssid",
2922                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2923                 false /* expectMetered */);
2924     }
2925 
2926     @Test
2927     public void testUpstreamHotspotDetection_incorrectOUI() throws Exception {
2928         byte[] data = new byte[10];
2929         new Random().nextBytes(data);
2930         doUpstreamHotspotDetectionTest(TEST_VENDOR_SPECIFIC_IE_ID, "\"ssid\"", "ssid",
2931                 new byte[] { (byte) 0x00, (byte) 0x1A, (byte) 0x11 }, (byte) 0x06, data,
2932                 false /* expectMetered */);
2933     }
2934 
2935     @Test
2936     public void testUpstreamHotspotDetection_incorrectSsid() throws Exception {
2937         byte[] data = new byte[10];
2938         new Random().nextBytes(data);
2939         doUpstreamHotspotDetectionTest(TEST_VENDOR_SPECIFIC_IE_ID, "\"another ssid\"", "ssid",
2940                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2941                 false /* expectMetered */);
2942     }
2943 
2944     @Test
2945     public void testUpstreamHotspotDetection_incorrectType() throws Exception {
2946         byte[] data = new byte[10];
2947         new Random().nextBytes(data);
2948         doUpstreamHotspotDetectionTest(TEST_VENDOR_SPECIFIC_IE_ID, "\"ssid\"", "ssid",
2949                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x0a, data,
2950                 false /* expectMetered */);
2951     }
2952 
2953     @Test
2954     public void testUpstreamHotspotDetection_zeroLengthData() throws Exception {
2955         byte[] data = new byte[0];
2956         doUpstreamHotspotDetectionTest(TEST_VENDOR_SPECIFIC_IE_ID, "\"ssid\"", "ssid",
2957                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2958                 true /* expectMetered */);
2959     }
2960 
2961     private void forceLayer2Roaming() throws Exception {
2962         final Layer2InformationParcelable roamingInfo = new Layer2InformationParcelable();
2963         roamingInfo.bssid = MacAddress.fromString(TEST_DHCP_ROAM_BSSID);
2964         roamingInfo.l2Key = TEST_DHCP_ROAM_L2KEY;
2965         roamingInfo.cluster = TEST_DHCP_ROAM_CLUSTER;
2966         mIIpClient.updateLayer2Information(roamingInfo);
2967     }
2968 
2969     private void assertDhcpRequestForReacquire(final DhcpPacket packet) {
2970         assertTrue(packet instanceof DhcpRequestPacket);
2971         assertEquals(packet.mClientIp, CLIENT_ADDR);    // client IP
2972         assertNull(packet.mRequestedIp);                // requested IP option
2973         assertNull(packet.mServerIdentifier);           // server ID
2974     }
2975 
2976     private void doDhcpRoamingTest(final boolean hasMismatchedIpAddress, final String displayName,
2977             final MacAddress bssid, final boolean expectRoaming,
2978             final boolean shouldReplyNakOnRoam) throws Exception {
2979         long currentTime = System.currentTimeMillis();
2980         final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, bssid);
2981 
2982         doAnswer(invocation -> {
2983             // we don't rely on the Init-Reboot state to renew previous cached IP lease.
2984             // Just return null and force state machine enter INIT state.
2985             final String l2Key = invocation.getArgument(0);
2986             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
2987                     .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null);
2988             return null;
2989         }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any());
2990 
2991         mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */,
2992                 null /* hostname */);
2993         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2994                 false /* isDhcpRapidCommitEnabled */,
2995                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */,
2996                 null /* captivePortalApiUrl */, displayName, null /* scanResultInfo */,
2997                 layer2Info);
2998         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2999         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
3000 
3001         // simulate the roaming by updating bssid.
3002         forceLayer2Roaming();
3003 
3004         currentTime = System.currentTimeMillis();
3005         reset(mIpMemoryStore);
3006         reset(mCb);
3007         if (!expectRoaming) {
3008             assertIpMemoryNeverStoreNetworkAttributes();
3009             return;
3010         }
3011         // check DHCPREQUEST broadcast sent to renew IP address.
3012         final DhcpPacket packet = getNextDhcpPacket();
3013         assertDhcpRequestForReacquire(packet);
3014 
3015         final ByteBuffer packetBuffer = shouldReplyNakOnRoam
3016                 ? buildDhcpNakPacket(packet, "request IP on a wrong subnet")
3017                 : buildDhcpAckPacket(packet,
3018                         hasMismatchedIpAddress ? CLIENT_ADDR_NEW : CLIENT_ADDR,
3019                         TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU,
3020                         false /* rapidCommit */, null /* captivePortalApiUrl */);
3021         mPacketReader.sendResponse(packetBuffer);
3022         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
3023 
3024         if (shouldReplyNakOnRoam) {
3025             ArgumentCaptor<ReachabilityLossInfoParcelable> lossInfoCaptor =
3026                     ArgumentCaptor.forClass(ReachabilityLossInfoParcelable.class);
3027             verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityFailure(lossInfoCaptor.capture());
3028             assertEquals(ReachabilityLossReason.ROAM, lossInfoCaptor.getValue().reason);
3029 
3030             // IPv4 address will be still deleted when DhcpClient state machine exits from
3031             // DhcpHaveLeaseState, a following onProvisioningFailure will be thrown then.
3032             // Also check DhcpClient won't send any DHCPDISCOVER packet.
3033             verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
3034             assertNull(getNextDhcpPacket(TEST_TIMEOUT_MS));
3035             verify(mCb, never()).onNewDhcpResults(any());
3036         } else if (hasMismatchedIpAddress) {
3037             ArgumentCaptor<DhcpResultsParcelable> resultsCaptor =
3038                     ArgumentCaptor.forClass(DhcpResultsParcelable.class);
3039             verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(resultsCaptor.capture());
3040             final DhcpResultsParcelable lease = resultsCaptor.getValue();
3041             assertNull(lease);
3042 
3043             // DhcpClient rolls back to StoppedState instead of INIT state after calling
3044             // notifyFailure, DHCPDISCOVER should not be sent out.
3045             assertNull(getNextDhcpPacket(TEST_TIMEOUT_MS));
3046         } else {
3047             assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime,
3048                     TEST_DEFAULT_MTU);
3049         }
3050     }
3051 
3052     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3053     public void testDhcpRoaming() throws Exception {
3054         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
3055                 MacAddress.fromString(TEST_DEFAULT_BSSID), true /* expectRoaming */,
3056                 false /* shouldReplyNakOnRoam */);
3057     }
3058 
3059     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3060     public void testDhcpRoaming_invalidBssid() throws Exception {
3061         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
3062                 MacAddress.fromString(TEST_DHCP_ROAM_BSSID), false /* expectRoaming */,
3063                 false/* shouldReplyNakOnRoam */);
3064     }
3065 
3066     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3067     public void testDhcpRoaming_nullBssid() throws Exception {
3068         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
3069                 null /* BSSID */, false /* expectRoaming */, false /* shouldReplyNakOnRoam */);
3070     }
3071 
3072     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3073     public void testDhcpRoaming_invalidDisplayName() throws Exception {
3074         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"test-ssid\"" /* display name */,
3075                 MacAddress.fromString(TEST_DEFAULT_BSSID), false /* expectRoaming */,
3076                 false /* shouldReplyNakOnRoam */);
3077     }
3078 
3079     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3080     public void testDhcpRoaming_mismatchedLeasedIpAddress() throws Exception {
3081         doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
3082                 MacAddress.fromString(TEST_DEFAULT_BSSID), true /* expectRoaming */,
3083                 false /* shouldReplyNakOnRoam */);
3084     }
3085 
3086     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3087     public void testDhcpRoaming_failureLeaseOnNak() throws Exception {
3088         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
3089                 MacAddress.fromString(TEST_DEFAULT_BSSID), true /* expectRoaming */,
3090                 true /* shouldReplyNakOnRoam */);
3091     }
3092 
3093     private LinkProperties performDualStackProvisioning() throws Exception {
3094         final Inet6Address dnsServer = ipv6Addr(IPV6_OFF_LINK_DNS_SERVER);
3095         final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64");
3096         final ByteBuffer rdnss = buildRdnssOption(3600, IPV6_OFF_LINK_DNS_SERVER);
3097         final ByteBuffer slla = buildSllaOption();
3098         final ByteBuffer ra = buildRaPacket(pio, rdnss, slla);
3099 
3100         return performDualStackProvisioning(ra, dnsServer);
3101     }
3102 
3103     private LinkProperties performDualStackProvisioning(final ByteBuffer ra,
3104             final InetAddress dnsServer) throws Exception {
3105         final InOrder inOrder = inOrder(mCb);
3106         final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
3107 
3108         // Start IPv4 provisioning first and wait IPv4 provisioning to succeed, and then start
3109         // IPv6 provisioning, which is more realistic and avoid the flaky case of both IPv4 and
3110         // IPv6 provisioning complete at the same time.
3111         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
3112                 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
3113         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any());
3114 
3115         waitForRouterSolicitation();
3116         mPacketReader.sendResponse(ra);
3117 
3118         // Wait until we see both success IPv4 and IPv6 provisioning, then there would be 4
3119         // addresses in LinkProperties, they are IPv4 address, IPv6 link-local address, stable
3120         // privacy address and privacy address.
3121         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> {
3122             if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false;
3123             if (x.getLinkAddresses().size() != 4) return false;
3124             lpFuture.complete(x);
3125             return true;
3126         }));
3127 
3128         final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
3129         assertNotNull(lp);
3130         assertTrue(lp.getDnsServers().contains(dnsServer));
3131         assertTrue(lp.getDnsServers().contains(SERVER_ADDR));
3132         assertHasAddressThat("link-local address", lp, x -> x.getAddress().isLinkLocalAddress());
3133         assertHasAddressThat("privacy address", lp, this::isPrivacyAddress);
3134         assertHasAddressThat("stable privacy address", lp, this::isStablePrivacyAddress);
3135 
3136         return lp;
3137     }
3138 
3139     private LinkProperties doDualStackProvisioning() throws Exception {
3140         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
3141                 .withoutIpReachabilityMonitor()
3142                 .build();
3143 
3144         // Enable rapid commit to accelerate DHCP handshake to shorten test duration,
3145         // not strictly necessary.
3146         setDhcpFeatures(true /* isRapidCommitEnabled */, false /* isDhcpIpConflictDetectEnabled */);
3147         // Both signature and root tests can use this function to do dual-stack provisioning.
3148         if (useNetworkStackSignature()) {
3149             mIpc.startProvisioning(config);
3150         } else {
3151             mIIpClient.startProvisioning(config.toStableParcelable());
3152         }
3153 
3154         return performDualStackProvisioning();
3155     }
3156 
3157     private boolean hasRouteTo(@NonNull final LinkProperties lp, @NonNull final String prefix) {
3158         return hasRouteTo(lp, prefix, RTN_UNICAST);
3159     }
3160 
3161     private boolean hasRouteTo(@NonNull final LinkProperties lp, @NonNull final String prefix,
3162             int type) {
3163         for (RouteInfo r : lp.getRoutes()) {
3164             if (r.getDestination().equals(new IpPrefix(prefix))) return r.getType() == type;
3165         }
3166         return false;
3167     }
3168 
3169     private boolean hasIpv6AddressPrefixedWith(@NonNull final LinkProperties lp,
3170             @NonNull final IpPrefix prefix) {
3171         for (LinkAddress la : lp.getLinkAddresses()) {
3172             final InetAddress addr = la.getAddress();
3173             if ((addr instanceof Inet6Address) && !addr.isLinkLocalAddress()) {
3174                 if (prefix.contains(addr)) return true;
3175             }
3176         }
3177         return false;
3178     }
3179 
3180     @Test
3181     @SignatureRequiredTest(reason = "Out of SLO flakiness")
3182     public void testIgnoreIpv6ProvisioningLoss_disableAcceptRaDefrtr() throws Exception {
3183         LinkProperties lp = doDualStackProvisioning();
3184         Log.d(TAG, "current LinkProperties: " + lp);
3185 
3186         final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
3187 
3188         // Send RA with 0-lifetime and wait until all global IPv6 addresses, IPv6-related default
3189         // route and DNS servers have been removed, then verify if there is IPv4-only, IPv6 link
3190         // local address and route to fe80::/64 info left in the LinkProperties.
3191         sendRouterAdvertisementWithZeroRouterLifetime();
3192         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(
3193                 argThat(x -> {
3194                     // Only IPv4 provisioned and IPv6 link-local address
3195                     final boolean isIPv6LinkLocalAndIPv4OnlyProvisioned =
3196                             (x.getLinkAddresses().size() == 2
3197                                     && x.getDnsServers().size() == 1
3198                                     && x.getAddresses().get(0) instanceof Inet4Address
3199                                     && x.getDnsServers().get(0) instanceof Inet4Address);
3200 
3201                     if (!isIPv6LinkLocalAndIPv4OnlyProvisioned) return false;
3202                     lpFuture.complete(x);
3203                     return true;
3204                 }));
3205         lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
3206         Log.d(TAG, "After receiving RA with 0 router lifetime, LinkProperties: " + lp);
3207         assertNotNull(lp);
3208         assertEquals(lp.getAddresses().get(0), CLIENT_ADDR);
3209         assertEquals(lp.getDnsServers().get(0), SERVER_ADDR);
3210         assertTrue(hasRouteTo(lp, IPV6_LINK_LOCAL_PREFIX)); // fe80::/64
3211         assertTrue(hasRouteTo(lp, IPV4_TEST_SUBNET_PREFIX)); // IPv4 directly-connected route
3212         assertTrue(hasRouteTo(lp, IPV4_ANY_ADDRESS_PREFIX)); // IPv4 default route
3213         assertTrue(lp.getAddresses().get(1).isLinkLocalAddress());
3214 
3215         clearInvocations(mCb);
3216 
3217         // Wait for RS after IPv6 stack has been restarted and reply with a normal RA to verify
3218         // that device gains the IPv6 provisioning without default route and off-link DNS server.
3219         sendBasicRouterAdvertisement(true /* waitForRs */);
3220         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(
3221                 x -> x.hasGlobalIpv6Address()
3222                         // IPv4, IPv6 link local, privacy and stable privacy
3223                         && x.getLinkAddresses().size() == 4
3224                         && !x.hasIpv6DefaultRoute()
3225                         && x.getDnsServers().size() == 1
3226                         && x.getDnsServers().get(0).equals(SERVER_ADDR)));
3227     }
3228 
3229     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3230     public void testDualStackProvisioning() throws Exception {
3231         doDualStackProvisioning();
3232 
3233         verify(mCb, never()).onProvisioningFailure(any());
3234     }
3235 
3236     private DhcpPacket verifyDhcpPacketRequestsIPv6OnlyPreferredOption(
3237             Class<? extends DhcpPacket> packetType) throws Exception {
3238         final DhcpPacket packet = getNextDhcpPacket();
3239         assertTrue(packetType.isInstance(packet));
3240         assertTrue(packet.hasRequestedParam(DHCP_IPV6_ONLY_PREFERRED));
3241         return packet;
3242     }
3243 
3244     private void doIPv6OnlyPreferredOptionTest(final Integer ipv6OnlyWaitTime,
3245             final Inet4Address clientAddress) throws Exception {
3246         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
3247                 .withoutIpReachabilityMonitor()
3248                 .build();
3249         setDhcpFeatures(false /* isRapidCommitEnabled */,
3250                 false /* isDhcpIpConflictDetectEnabled */);
3251         startIpClientProvisioning(config);
3252 
3253         final DhcpPacket packet =
3254                 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
3255 
3256         // Respond DHCPOFFER with IPv6-Only preferred option and offered address.
3257         mPacketReader.sendResponse(buildDhcpOfferPacket(packet, clientAddress,
3258                 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, null /* captivePortalUrl */,
3259                 ipv6OnlyWaitTime, null /* domainName */, null /* domainSearchList */));
3260     }
3261 
3262     private void doDiscoverIPv6OnlyPreferredOptionTest(final int optionSecs,
3263             final long expectedWaitSecs) throws Exception {
3264         doIPv6OnlyPreferredOptionTest(optionSecs, CLIENT_ADDR);
3265         final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT",
3266                 expectedWaitSecs, mDependencies.mDhcpClient.getHandler());
3267         mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm());
3268         // Implicitly check that the client never sent a DHCPREQUEST to request the offered address.
3269         verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
3270     }
3271 
3272     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3273     public void testDiscoverIPv6OnlyPreferredOption() throws Exception {
3274         doDiscoverIPv6OnlyPreferredOptionTest(TEST_IPV6_ONLY_WAIT_S, TEST_IPV6_ONLY_WAIT_S);
3275     }
3276 
3277     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3278     public void testDiscoverIPv6OnlyPreferredOption_LowerIPv6OnlyWait() throws Exception {
3279         doDiscoverIPv6OnlyPreferredOptionTest(TEST_LOWER_IPV6_ONLY_WAIT_S,
3280                 TEST_LOWER_IPV6_ONLY_WAIT_S);
3281     }
3282 
3283     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3284     public void testDiscoverIPv6OnlyPreferredOption_ZeroIPv6OnlyWait() throws Exception {
3285         doDiscoverIPv6OnlyPreferredOptionTest(TEST_ZERO_IPV6_ONLY_WAIT_S,
3286                 TEST_LOWER_IPV6_ONLY_WAIT_S);
3287     }
3288 
3289     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3290     public void testDiscoverIPv6OnlyPreferredOption_MaxIPv6OnlyWait() throws Exception {
3291         doDiscoverIPv6OnlyPreferredOptionTest((int) TEST_MAX_IPV6_ONLY_WAIT_S, 0xffffffffL);
3292     }
3293 
3294     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3295     public void testDiscoverIPv6OnlyPreferredOption_ZeroIPv6OnlyWaitWithOfferedAnyAddress()
3296             throws Exception {
3297         doIPv6OnlyPreferredOptionTest(TEST_ZERO_IPV6_ONLY_WAIT_S, IPV4_ADDR_ANY);
3298 
3299         final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 300,
3300                 mDependencies.mDhcpClient.getHandler());
3301         mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm());
3302 
3303         verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
3304     }
3305 
3306     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3307     public void testDiscoverIPv6OnlyPreferredOption_enabledPreconnection() throws Exception {
3308         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
3309                 .withoutIpReachabilityMonitor()
3310                 .withPreconnection()
3311                 .build();
3312 
3313         setDhcpFeatures(true /* isRapidCommitEnabled */, false /* isDhcpIpConflictDetectEnabled */);
3314         startIpClientProvisioning(config);
3315 
3316         final DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart();
3317         verify(mCb).setNeighborDiscoveryOffload(true);
3318 
3319         // Force IpClient transition to RunningState from PreconnectionState.
3320         mIpc.notifyPreconnectionComplete(true /* success */);
3321         HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
3322         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true);
3323 
3324         // DHCP server SHOULD NOT honor the Rapid-Commit option if the response would
3325         // contain the IPv6-only Preferred option to the client, instead respond with
3326         // a DHCPOFFER.
3327         mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR, TEST_LEASE_DURATION_S,
3328                 (short) TEST_DEFAULT_MTU, null /* captivePortalUrl */, TEST_IPV6_ONLY_WAIT_S,
3329                 null /* domainName */, null /* domainSearchList */));
3330 
3331         final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 1800,
3332                 mDependencies.mDhcpClient.getHandler());
3333         mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm());
3334 
3335         verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
3336     }
3337 
3338     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3339     public void testDiscoverIPv6OnlyPreferredOption_NoIPv6OnlyPreferredOption() throws Exception {
3340         doIPv6OnlyPreferredOptionTest(null /* ipv6OnlyWaitTime */, CLIENT_ADDR);
3341 
3342         // The IPv6-only Preferred option SHOULD be included in the Parameter Request List option
3343         // in DHCPREQUEST messages after receiving a DHCPOFFER without this option.
3344         verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpRequestPacket.class);
3345     }
3346 
3347     private void setUpRetrievedNetworkAttributesForInitRebootState() {
3348         final NetworkAttributes na = new NetworkAttributes.Builder()
3349                 .setAssignedV4Address(CLIENT_ADDR)
3350                 .setAssignedV4AddressExpiry(Long.MAX_VALUE) // lease is always valid
3351                 .setMtu(new Integer(TEST_DEFAULT_MTU))
3352                 .setCluster(TEST_CLUSTER)
3353                 .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
3354                 .build();
3355         storeNetworkAttributes(TEST_L2KEY, na);
3356     }
3357 
3358     private void startFromInitRebootStateWithIPv6OnlyPreferredOption(final Integer ipv6OnlyWaitTime,
3359             final long expectedWaitSecs) throws Exception {
3360         setUpRetrievedNetworkAttributesForInitRebootState();
3361 
3362         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
3363                 .withoutIpReachabilityMonitor()
3364                 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
3365                           MacAddress.fromString(TEST_DEFAULT_BSSID)))
3366                 .build();
3367 
3368         setDhcpFeatures(false /* isRapidCommitEnabled */,
3369                 false /* isDhcpIpConflictDetectEnabled */);
3370         startIpClientProvisioning(config);
3371 
3372         final DhcpPacket packet =
3373                 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpRequestPacket.class);
3374 
3375         // Respond DHCPACK with IPv6-Only preferred option.
3376         mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR,
3377                 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, false /* rapidcommit */,
3378                 null /* captivePortalUrl */, ipv6OnlyWaitTime, null /* domainName */,
3379                 null /* domainSearchList */));
3380 
3381         if (ipv6OnlyWaitTime != null) {
3382             expectAlarmSet(null /* inOrder */, "TIMEOUT", expectedWaitSecs,
3383                     mDependencies.mDhcpClient.getHandler());
3384         }
3385     }
3386 
3387     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3388     public void testRequestIPv6OnlyPreferredOption() throws Exception {
3389         startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_IPV6_ONLY_WAIT_S,
3390                 TEST_IPV6_ONLY_WAIT_S);
3391 
3392         // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
3393         // IPv6-Only preferred option(default value) in the DHCPACK packet.
3394         assertIpMemoryNeverStoreNetworkAttributes();
3395     }
3396 
3397     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3398     public void testRequestIPv6OnlyPreferredOption_LowerIPv6OnlyWait() throws Exception {
3399         startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_LOWER_IPV6_ONLY_WAIT_S,
3400                 TEST_LOWER_IPV6_ONLY_WAIT_S);
3401 
3402         // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
3403         // IPv6-Only preferred option(less than MIN_V6ONLY_WAIT_MS) in the DHCPACK packet.
3404         assertIpMemoryNeverStoreNetworkAttributes();
3405     }
3406 
3407     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3408     public void testRequestIPv6OnlyPreferredOption_ZeroIPv6OnlyWait() throws Exception {
3409         startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_ZERO_IPV6_ONLY_WAIT_S,
3410                 TEST_LOWER_IPV6_ONLY_WAIT_S);
3411 
3412         // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
3413         // IPv6-Only preferred option(0) in the DHCPACK packet.
3414         assertIpMemoryNeverStoreNetworkAttributes();
3415     }
3416 
3417     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3418     public void testRequestIPv6OnlyPreferredOption_MaxIPv6OnlyWait() throws Exception {
3419         startFromInitRebootStateWithIPv6OnlyPreferredOption((int) TEST_MAX_IPV6_ONLY_WAIT_S,
3420                 0xffffffffL);
3421 
3422         // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
3423         // IPv6-Only preferred option(MAX_UNSIGNED_INTEGER: 0xFFFFFFFF) in the DHCPACK packet.
3424         assertIpMemoryNeverStoreNetworkAttributes();
3425     }
3426 
3427     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
3428     public void testRequestIPv6OnlyPreferredOption_NoIPv6OnlyPreferredOption() throws Exception {
3429         final long currentTime = System.currentTimeMillis();
3430         startFromInitRebootStateWithIPv6OnlyPreferredOption(null /* ipv6OnlyWaitTime */,
3431                 0 /* expectedWaitSecs */);
3432 
3433         // Client processes DHCPACK packet normally and transits to the ConfiguringInterfaceState
3434         // due to the null V6ONLY_WAIT.
3435         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
3436     }
3437 
3438     private static int getNumOpenFds() {
3439         return new File("/proc/" + Os.getpid() + "/fd").listFiles().length;
3440     }
3441 
3442     private void shutdownAndRecreateIpClient() throws Exception {
3443         clearInvocations(mCb);
3444         mIpc.shutdown();
3445         awaitIpClientShutdown();
3446         mIpc = makeIpClient();
3447     }
3448 
3449     @Test @SignatureRequiredTest(reason = "Only counts FDs from the current process. TODO: fix")
3450     public void testNoFdLeaks() throws Exception {
3451         // Shut down and restart IpClient once to ensure that any fds that are opened the first
3452         // time it runs do not cause the test to fail.
3453         doDualStackProvisioning();
3454         shutdownAndRecreateIpClient();
3455 
3456         // Unfortunately we cannot use a large number of iterations as it would make the test run
3457         // too slowly. On crosshatch-eng each iteration takes ~250ms.
3458         final int iterations = 10;
3459         final int before = getNumOpenFds();
3460         for (int i = 0; i < iterations; i++) {
3461             doDualStackProvisioning();
3462             shutdownAndRecreateIpClient();
3463             // The last time this loop runs, mIpc will be shut down in tearDown.
3464         }
3465         final int after = getNumOpenFds();
3466 
3467         // Check that the number of open fds is the same as before, within some tolerance (e.g.,
3468         // garbage collection or other cleanups might have caused an fd to be closed). This
3469         // shouldn't make leak detection much less reliable, since it's likely that any leak would
3470         // at least leak one FD per loop.
3471         final int tolerance = 4;
3472         assertTrue(
3473                 "FD leak detected after " + iterations + " iterations: expected "
3474                         + before + " +/- " + tolerance + " fds, found " + after,
3475                 Math.abs(after - before) <= tolerance);
3476     }
3477 
3478     // TODO: delete when DhcpOption is @JavaOnlyImmutable.
3479     private static DhcpOption makeDhcpOption(final byte type, final byte[] value) {
3480         final DhcpOption opt = new DhcpOption();
3481         opt.type = type;
3482         opt.value = value;
3483         return opt;
3484     }
3485 
3486     private static final List<DhcpOption> TEST_OEM_DHCP_OPTIONS = Arrays.asList(
3487             // DHCP_USER_CLASS
3488             makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO),
3489             // DHCP_VENDOR_CLASS_ID
3490             makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes())
3491     );
3492 
3493     private DhcpPacket doCustomizedDhcpOptionsTest(final List<DhcpOption> options,
3494              final ScanResultInfo info) throws Exception {
3495         ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
3496                 .withoutIpReachabilityMonitor()
3497                 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
3498                         MacAddress.fromString(TEST_DEFAULT_BSSID)))
3499                 .withScanResultInfo(info)
3500                 .withDhcpOptions(options)
3501                 .withoutIPv6();
3502 
3503         setDhcpFeatures(false /* isRapidCommitEnabled */,
3504                 false /* isDhcpIpConflictDetectEnabled */);
3505 
3506         startIpClientProvisioning(prov.build());
3507         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true);
3508         verify(mCb, never()).onProvisioningFailure(any());
3509 
3510         return getNextDhcpPacket();
3511     }
3512 
3513     @Test
3514     public void testDiscoverCustomizedDhcpOptions() throws Exception {
3515         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3516                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3517         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3518 
3519         assertTrue(packet instanceof DhcpDiscoverPacket);
3520         assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
3521         assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO);
3522     }
3523 
3524     @Test
3525     public void testDiscoverCustomizedDhcpOptions_nullDhcpOptions() throws Exception {
3526         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3527                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3528         final DhcpPacket packet = doCustomizedDhcpOptionsTest(null /* options */, info);
3529 
3530         assertTrue(packet instanceof DhcpDiscoverPacket);
3531         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3532         assertNull(packet.mUserClass);
3533     }
3534 
3535     @Test
3536     public void testDiscoverCustomizedDhcpOptions_nullScanResultInfo() throws Exception {
3537         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS,
3538                 null /* scanResultInfo */);
3539 
3540         assertTrue(packet instanceof DhcpDiscoverPacket);
3541         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3542         assertNull(packet.mUserClass);
3543     }
3544 
3545     @Test
3546     public void testDiscoverCustomizedDhcpOptions_disallowedOui() throws Exception {
3547         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID,
3548                 new byte[]{ 0x00, 0x11, 0x22} /* oui */, TEST_VENDOR_SPECIFIC_IE_TYPE);
3549         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3550 
3551         assertTrue(packet instanceof DhcpDiscoverPacket);
3552         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3553         assertNull(packet.mUserClass);
3554     }
3555 
3556     @Test
3557     public void testDiscoverCustomizedDhcpOptions_invalidIeId() throws Exception {
3558         final ScanResultInfo info = makeScanResultInfo(0xde /* vendor-specific IE */, TEST_OEM_OUI,
3559                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3560         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3561 
3562         assertTrue(packet instanceof DhcpDiscoverPacket);
3563         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3564         assertNull(packet.mUserClass);
3565     }
3566 
3567     @Test
3568     public void testDiscoverCustomizedDhcpOptions_invalidVendorSpecificType() throws Exception {
3569         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3570                 (byte) 0x10 /* vendor-specific IE type */);
3571         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3572 
3573         assertTrue(packet instanceof DhcpDiscoverPacket);
3574         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3575         assertNull(packet.mUserClass);
3576     }
3577 
3578     @Test
3579     public void testDiscoverCustomizedDhcpOptions_legacyVendorSpecificType() throws Exception {
3580         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3581                  LEGACY_TEST_VENDOR_SPECIFIC_IE_TYPE);
3582         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3583 
3584         assertTrue(packet instanceof DhcpDiscoverPacket);
3585         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3586         assertNull(packet.mUserClass);
3587     }
3588 
3589     @Test
3590     public void testDisoverCustomizedDhcpOptions_disallowedOption() throws Exception {
3591         final List<DhcpOption> options = Arrays.asList(
3592                 makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()),
3593                 makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO),
3594                 // Option 26: MTU
3595                 makeDhcpOption((byte) 26, HexDump.toByteArray(TEST_DEFAULT_MTU)));
3596         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3597                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3598         final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
3599 
3600         assertTrue(packet instanceof DhcpDiscoverPacket);
3601         assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
3602         assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO);
3603         assertNull(packet.mMtu);
3604     }
3605 
3606     @Test
3607     public void testDiscoverCustomizedDhcpOptions_disallowedParamRequestOption() throws Exception {
3608         final List<DhcpOption> options = Arrays.asList(
3609                 makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()),
3610                 makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO),
3611                 // NTP_SERVER
3612                 makeDhcpOption((byte) 42, null));
3613         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3614                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3615         final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
3616 
3617         assertTrue(packet instanceof DhcpDiscoverPacket);
3618         assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
3619         assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO);
3620         assertFalse(packet.hasRequestedParam((byte) 42 /* NTP_SERVER */));
3621     }
3622 
3623     @Test
3624     public void testDiscoverCustomizedDhcpOptions_ParameterRequestListOnly() throws Exception {
3625         final List<DhcpOption> options = Arrays.asList(
3626                 // DHCP_USER_CLASS
3627                 makeDhcpOption((byte) 77, null));
3628         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3629                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3630         final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
3631 
3632         assertTrue(packet instanceof DhcpDiscoverPacket);
3633         assertTrue(packet.hasRequestedParam((byte) 77 /* DHCP_USER_CLASS */));
3634         assertNull(packet.mUserClass);
3635     }
3636 
3637     @Test
3638     public void testRequestCustomizedDhcpOptions() throws Exception {
3639         setUpRetrievedNetworkAttributesForInitRebootState();
3640 
3641         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3642                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3643         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3644 
3645         assertTrue(packet instanceof DhcpRequestPacket);
3646         assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
3647         assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO);
3648     }
3649 
3650     @Test
3651     public void testRequestCustomizedDhcpOptions_nullDhcpOptions() throws Exception {
3652         setUpRetrievedNetworkAttributesForInitRebootState();
3653 
3654         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3655                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3656         final DhcpPacket packet = doCustomizedDhcpOptionsTest(null /* options */, info);
3657 
3658         assertTrue(packet instanceof DhcpRequestPacket);
3659         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3660         assertNull(packet.mUserClass);
3661     }
3662 
3663     @Test
3664     public void testRequestCustomizedDhcpOptions_nullScanResultInfo() throws Exception {
3665         setUpRetrievedNetworkAttributesForInitRebootState();
3666 
3667         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS,
3668                 null /* scanResultInfo */);
3669 
3670         assertTrue(packet instanceof DhcpRequestPacket);
3671         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3672         assertNull(packet.mUserClass);
3673     }
3674 
3675     @Test
3676     public void testRequestCustomizedDhcpOptions_disallowedOui() throws Exception {
3677         setUpRetrievedNetworkAttributesForInitRebootState();
3678 
3679         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID,
3680                 new byte[]{ 0x00, 0x11, 0x22} /* oui */, TEST_VENDOR_SPECIFIC_IE_TYPE);
3681         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3682 
3683         assertTrue(packet instanceof DhcpRequestPacket);
3684         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3685         assertNull(packet.mUserClass);
3686     }
3687 
3688     @Test
3689     public void testRequestCustomizedDhcpOptions_invalidIeId() throws Exception {
3690         setUpRetrievedNetworkAttributesForInitRebootState();
3691 
3692         final ScanResultInfo info = makeScanResultInfo(0xde /* vendor-specific IE */, TEST_OEM_OUI,
3693                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3694         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3695 
3696         assertTrue(packet instanceof DhcpRequestPacket);
3697         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3698         assertNull(packet.mUserClass);
3699     }
3700 
3701     @Test
3702     public void testRequestCustomizedDhcpOptions_invalidVendorSpecificType() throws Exception {
3703         setUpRetrievedNetworkAttributesForInitRebootState();
3704 
3705         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3706                 (byte) 0x20 /* vendor-specific IE type */);
3707         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3708 
3709         assertTrue(packet instanceof DhcpRequestPacket);
3710         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3711         assertNull(packet.mUserClass);
3712     }
3713 
3714     @Test
3715     public void testRequestCustomizedDhcpOptions_legacyVendorSpecificType() throws Exception {
3716         setUpRetrievedNetworkAttributesForInitRebootState();
3717 
3718         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3719                 LEGACY_TEST_VENDOR_SPECIFIC_IE_TYPE);
3720         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
3721 
3722         assertTrue(packet instanceof DhcpRequestPacket);
3723         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
3724         assertNull(packet.mUserClass);
3725     }
3726 
3727     @Test
3728     public void testRequestCustomizedDhcpOptions_disallowedOption() throws Exception {
3729         setUpRetrievedNetworkAttributesForInitRebootState();
3730 
3731         final List<DhcpOption> options = Arrays.asList(
3732                 makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()),
3733                 makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO),
3734                 // Option 26: MTU
3735                 makeDhcpOption((byte) 26, HexDump.toByteArray(TEST_DEFAULT_MTU)));
3736         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3737                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3738         final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
3739 
3740         assertTrue(packet instanceof DhcpRequestPacket);
3741         assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
3742         assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO);
3743         assertNull(packet.mMtu);
3744     }
3745 
3746     @Test
3747     public void testRequestCustomizedDhcpOptions_disallowedParamRequestOption() throws Exception {
3748         setUpRetrievedNetworkAttributesForInitRebootState();
3749 
3750         final List<DhcpOption> options = Arrays.asList(
3751                 makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()),
3752                 makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO),
3753                 // NTP_SERVER
3754                 makeDhcpOption((byte) 42, null));
3755         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3756                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3757         final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
3758 
3759         assertTrue(packet instanceof DhcpRequestPacket);
3760         assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
3761         assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO);
3762         assertFalse(packet.hasRequestedParam((byte) 42 /* NTP_SERVER */));
3763     }
3764 
3765     @Test
3766     public void testRequestCustomizedDhcpOptions_ParameterRequestListOnly() throws Exception {
3767         setUpRetrievedNetworkAttributesForInitRebootState();
3768 
3769         final List<DhcpOption> options = Arrays.asList(
3770                 // DHCP_USER_CLASS
3771                 makeDhcpOption((byte) 77, null));
3772         final ScanResultInfo info = makeScanResultInfo(TEST_VENDOR_SPECIFIC_IE_ID, TEST_OEM_OUI,
3773                 TEST_VENDOR_SPECIFIC_IE_TYPE);
3774         final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
3775 
3776         assertTrue(packet instanceof DhcpRequestPacket);
3777         assertTrue(packet.hasRequestedParam((byte) 77 /* DHCP_USER_CLASS */));
3778         assertNull(packet.mUserClass);
3779     }
3780 
3781     private void assertGratuitousNa(final NeighborAdvertisement na) throws Exception {
3782         final MacAddress etherMulticast =
3783                 NetworkStackUtils.ipv6MulticastToEthernetMulticast(IPV6_ADDR_ALL_ROUTERS_MULTICAST);
3784         final LinkAddress target = new LinkAddress(na.naHdr.target, 64);
3785 
3786         assertEquals(etherMulticast, na.ethHdr.dstMac);
3787         assertEquals(ETH_P_IPV6, na.ethHdr.etherType);
3788         assertEquals(IPPROTO_ICMPV6, na.ipv6Hdr.nextHeader);
3789         assertEquals(0xff, na.ipv6Hdr.hopLimit);
3790         assertTrue(na.ipv6Hdr.srcIp.isLinkLocalAddress());
3791         assertEquals(IPV6_ADDR_ALL_ROUTERS_MULTICAST, na.ipv6Hdr.dstIp);
3792         assertEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, na.icmpv6Hdr.type);
3793         assertEquals(0, na.icmpv6Hdr.code);
3794         assertEquals(0, na.naHdr.flags);
3795         assertTrue(target.isGlobalPreferred());
3796     }
3797 
3798     private void assertMulticastNsFromIpv6Gua(final NeighborSolicitation ns) throws Exception {
3799         final Inet6Address solicitedNodeMulticast =
3800                 NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(ROUTER_LINK_LOCAL);
3801         final MacAddress etherMulticast =
3802                 NetworkStackUtils.ipv6MulticastToEthernetMulticast(solicitedNodeMulticast);
3803 
3804         assertEquals(etherMulticast, ns.ethHdr.dstMac);
3805         assertEquals(ETH_P_IPV6, ns.ethHdr.etherType);
3806         assertEquals(IPPROTO_ICMPV6, ns.ipv6Hdr.nextHeader);
3807         assertEquals(0xff, ns.ipv6Hdr.hopLimit);
3808 
3809         final LinkAddress srcIp = new LinkAddress(ns.ipv6Hdr.srcIp.getHostAddress() + "/64");
3810         assertTrue(srcIp.isGlobalPreferred());
3811         assertEquals(solicitedNodeMulticast, ns.ipv6Hdr.dstIp);
3812         assertEquals(ICMPV6_NEIGHBOR_SOLICITATION, ns.icmpv6Hdr.type);
3813         assertEquals(0, ns.icmpv6Hdr.code);
3814         assertEquals(ROUTER_LINK_LOCAL, ns.nsHdr.target);
3815     }
3816 
3817     @Test
3818     public void testGratuitousNaForNewGlobalUnicastAddresses() throws Exception {
3819         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
3820                 .withoutIpReachabilityMonitor()
3821                 .withoutIPv4()
3822                 .build();
3823 
3824         startIpClientProvisioning(config);
3825 
3826         doIpv6OnlyProvisioning();
3827 
3828         final List<NeighborAdvertisement> naList = new ArrayList<>();
3829         NeighborAdvertisement packet;
3830         while ((packet = getNextNeighborAdvertisement()) != null) {
3831             assertGratuitousNa(packet);
3832             naList.add(packet);
3833         }
3834         assertEquals(2, naList.size()); // privacy address and stable privacy address
3835     }
3836 
3837     private void startGratuitousArpAndNaAfterRoamingTest(boolean isGratuitousArpNaRoamingEnabled,
3838             boolean hasIpv4, boolean hasIpv6) throws Exception {
3839         final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
3840                 MacAddress.fromString(TEST_DEFAULT_BSSID));
3841         final ScanResultInfo scanResultInfo =
3842                 makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID);
3843         final ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
3844                 .withoutIpReachabilityMonitor()
3845                 .withLayer2Information(layer2Info)
3846                 .withScanResultInfo(scanResultInfo)
3847                 .withDisplayName("ssid");
3848         if (!hasIpv4) prov.withoutIPv4();
3849         if (!hasIpv6) prov.withoutIPv6();
3850 
3851         // Enable rapid commit to accelerate DHCP handshake to shorten test duration,
3852         // not strictly necessary.
3853         setDhcpFeatures(true /* isRapidCommitEnabled */,
3854                 false /* isDhcpIpConflictDetectEnabled */);
3855 
3856         if (isGratuitousArpNaRoamingEnabled) {
3857             setFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, true);
3858         } else {
3859             setFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, false);
3860         }
3861         startIpClientProvisioning(prov.build());
3862     }
3863 
3864     private void waitForGratuitousArpAndNaPacket(final List<ArpPacket> arpList,
3865             final List<NeighborAdvertisement> naList) throws Exception {
3866         NeighborAdvertisement na;
3867         ArpPacket garp;
3868         do {
3869             na = getNextNeighborAdvertisement();
3870             if (na != null) {
3871                 assertGratuitousNa(na);
3872                 naList.add(na);
3873             }
3874             garp = getNextArpPacket(TEST_TIMEOUT_MS);
3875             if (garp != null) {
3876                 assertGratuitousARP(garp);
3877                 arpList.add(garp);
3878             }
3879         } while (na != null || garp != null);
3880     }
3881 
3882     @Test
3883     public void testGratuitousArpAndNaAfterRoaming() throws Exception {
3884         startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */,
3885                 true /* hasIpv4 */, true /* hasIpv6 */);
3886         performDualStackProvisioning();
3887         forceLayer2Roaming();
3888 
3889         final List<ArpPacket> arpList = new ArrayList<>();
3890         final List<NeighborAdvertisement> naList = new ArrayList<>();
3891         waitForGratuitousArpAndNaPacket(arpList, naList);
3892         // 2 NAs sent due to RFC9131 implement and 2 NAs sent after roam
3893         assertEquals(4, naList.size()); // privacy address and stable privacy address
3894         assertEquals(1, arpList.size()); // IPv4 address
3895     }
3896 
3897     @Test
3898     public void testGratuitousArpAndNaAfterRoaming_disableExpFlag() throws Exception {
3899         startGratuitousArpAndNaAfterRoamingTest(false /* isGratuitousArpNaRoamingEnabled */,
3900                 true /* hasIpv4 */, true /* hasIpv6 */);
3901         performDualStackProvisioning();
3902         forceLayer2Roaming();
3903 
3904         final List<ArpPacket> arpList = new ArrayList<>();
3905         final List<NeighborAdvertisement> naList = new ArrayList<>();
3906         waitForGratuitousArpAndNaPacket(arpList, naList);
3907         assertEquals(2, naList.size()); // NAs sent due to RFC9131 implement, not from roam
3908         assertEquals(0, arpList.size());
3909     }
3910 
3911     @Test
3912     public void testGratuitousArpAndNaAfterRoaming_IPv6OnlyNetwork() throws Exception {
3913         startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */,
3914                 false /* hasIpv4 */, true /* hasIpv6 */);
3915         doIpv6OnlyProvisioning();
3916         forceLayer2Roaming();
3917 
3918         final List<ArpPacket> arpList = new ArrayList<>();
3919         final List<NeighborAdvertisement> naList = new ArrayList<>();
3920         waitForGratuitousArpAndNaPacket(arpList, naList);
3921         // 2 NAs sent due to RFC9131 implement and 2 NAs sent after roam
3922         assertEquals(4, naList.size());
3923         assertEquals(0, arpList.size());
3924     }
3925 
3926     @Test
3927     public void testGratuitousArpAndNaAfterRoaming_IPv4OnlyNetwork() throws Exception {
3928         startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */,
3929                 true /* hasIpv4 */, false /* hasIpv6 */);
3930 
3931         // Start IPv4 provisioning and wait until entire provisioning completes.
3932         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
3933                 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
3934         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
3935         forceLayer2Roaming();
3936 
3937         final List<ArpPacket> arpList = new ArrayList<>();
3938         final List<NeighborAdvertisement> naList = new ArrayList<>();
3939         waitForGratuitousArpAndNaPacket(arpList, naList);
3940         assertEquals(0, naList.size());
3941         assertEquals(1, arpList.size());
3942     }
3943 
3944     private void assertNeighborSolicitation(final NeighborSolicitation ns,
3945             final Inet6Address target) {
3946         assertEquals(ETH_P_IPV6, ns.ethHdr.etherType);
3947         assertEquals(IPPROTO_ICMPV6, ns.ipv6Hdr.nextHeader);
3948         assertEquals(0xff, ns.ipv6Hdr.hopLimit);
3949         assertTrue(ns.ipv6Hdr.srcIp.isLinkLocalAddress());
3950         assertEquals(ICMPV6_NEIGHBOR_SOLICITATION, ns.icmpv6Hdr.type);
3951         assertEquals(0, ns.icmpv6Hdr.code);
3952         assertEquals(0, ns.nsHdr.reserved);
3953         assertEquals(target, ns.nsHdr.target);
3954         assertEquals(ns.slla.linkLayerAddress, ns.ethHdr.srcMac);
3955     }
3956 
3957     private void assertUnicastNeighborSolicitation(final NeighborSolicitation ns,
3958             final MacAddress dstMac, final Inet6Address dstIp, final Inet6Address target) {
3959         assertEquals(dstMac, ns.ethHdr.dstMac);
3960         assertEquals(dstIp, ns.ipv6Hdr.dstIp);
3961         assertNeighborSolicitation(ns, target);
3962     }
3963 
3964     private void assertMulticastNeighborSolicitation(final NeighborSolicitation ns,
3965             final Inet6Address target) {
3966         final MacAddress etherMulticast =
3967                 NetworkStackUtils.ipv6MulticastToEthernetMulticast(ns.ipv6Hdr.dstIp);
3968         assertEquals(etherMulticast, ns.ethHdr.dstMac);
3969         assertTrue(ns.ipv6Hdr.dstIp.isMulticastAddress());
3970         assertNeighborSolicitation(ns, target);
3971     }
3972 
3973     private NeighborSolicitation waitForUnicastNeighborSolicitation(final MacAddress dstMac,
3974             final Inet6Address dstIp, final Inet6Address targetIp) throws Exception {
3975         NeighborSolicitation ns;
3976         while ((ns = getNextNeighborSolicitation()) != null) {
3977             // Filter out the multicast NSes used for duplicate address detetction, the target
3978             // address is the global IPv6 address inside these NSes, and multicast NSes sent from
3979             // device's GUAs to force first-hop router to update the neighbor cache entry.
3980             if (ns.ipv6Hdr.srcIp.isLinkLocalAddress() && ns.nsHdr.target.isLinkLocalAddress()) {
3981                 break;
3982             }
3983         }
3984         assertNotNull("No unicast Neighbor solicitation received on interface within timeout", ns);
3985         assertUnicastNeighborSolicitation(ns, dstMac, dstIp, targetIp);
3986         return ns;
3987     }
3988 
3989     private List<NeighborSolicitation> waitForMultipleNeighborSolicitations() throws Exception {
3990         NeighborSolicitation ns;
3991         final List<NeighborSolicitation> nsList = new ArrayList<NeighborSolicitation>();
3992         while ((ns = getNextNeighborSolicitation()) != null) {
3993             // Filter out the multicast NSes used for duplicate address detetction, the target
3994             // address is the global IPv6 address inside these NSes, and multicast NSes sent from
3995             // device's GUAs to force first-hop router to update the neighbor cache entry.
3996             if (ns.ipv6Hdr.srcIp.isLinkLocalAddress() && ns.nsHdr.target.isLinkLocalAddress()) {
3997                 nsList.add(ns);
3998             }
3999         }
4000         assertFalse(nsList.isEmpty());
4001         return nsList;
4002     }
4003 
4004     private NeighborSolicitation expectDadNeighborSolicitationForLinkLocal(boolean shouldDisableDad)
4005             throws Exception {
4006         final NeighborSolicitation ns = getNextNeighborSolicitation();
4007         if (!shouldDisableDad) {
4008             final Inet6Address solicitedNodeMulticast =
4009                     NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(ns.nsHdr.target);
4010             assertNotNull("No multicast NS received on interface within timeout", ns);
4011             assertEquals(IPV6_ADDR_ANY, ns.ipv6Hdr.srcIp);     // srcIp: ::/
4012             assertTrue(ns.ipv6Hdr.dstIp.isMulticastAddress()); // dstIp: solicited-node mcast
4013             assertTrue(ns.ipv6Hdr.dstIp.equals(solicitedNodeMulticast));
4014             assertTrue(ns.nsHdr.target.isLinkLocalAddress());  // targetIp: IPv6 LL address
4015         } else {
4016             assertNull(ns);
4017         }
4018         return ns;
4019     }
4020 
4021     // Override this function with disabled experiment flag by default, in order not to
4022     // affect those tests which are just related to basic IpReachabilityMonitor infra.
4023     private void prepareIpReachabilityMonitorTest() throws Exception {
4024         prepareIpReachabilityMonitorTest(false /* isMulticastResolicitEnabled */);
4025     }
4026 
4027     private void assertNotifyNeighborLost(Inet6Address targetIp, NudEventType eventType)
4028             throws Exception {
4029         // For root test suite, rely on the IIpClient aidl interface version constant defined in
4030         // {@link IpClientRootTest.BinderCbWrapper}; for privileged integration test suite that
4031         // requires signature permission, use the mocked aidl version defined in {@link setUpMocks},
4032         // which results in only new callbacks are verified. And add separate test cases to test the
4033         // legacy callbacks explicitly as well.
4034         assertNeighborReachabilityLoss(targetIp, eventType,
4035                 useNetworkStackSignature()
4036                         ? IpClient.VERSION_ADDED_REACHABILITY_FAILURE
4037                         : mIIpClient.getInterfaceVersion());
4038     }
4039 
4040     private void assertNeighborReachabilityLoss(Inet6Address targetIp, NudEventType eventType,
4041             int targetAidlVersion) throws Exception {
4042         if (targetAidlVersion >= IpClient.VERSION_ADDED_REACHABILITY_FAILURE) {
4043             final ArgumentCaptor<ReachabilityLossInfoParcelable> lossInfoCaptor =
4044                     ArgumentCaptor.forClass(ReachabilityLossInfoParcelable.class);
4045             verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityFailure(lossInfoCaptor.capture());
4046             assertEquals(nudEventTypeToInt(eventType), lossInfoCaptor.getValue().reason);
4047             verify(mCb, never()).onReachabilityLost(any());
4048         } else {
4049             verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any());
4050             verify(mCb, never()).onReachabilityFailure(any());
4051         }
4052     }
4053 
4054     private void assertNeverNotifyNeighborLost() throws Exception {
4055         verify(mCb, never()).onReachabilityFailure(any());
4056         verify(mCb, never()).onReachabilityLost(any());
4057     }
4058 
4059     private void prepareIpReachabilityMonitorTest(boolean isMulticastResolicitEnabled)
4060             throws Exception {
4061         final ScanResultInfo info = makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID);
4062         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4063                 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
4064                        MacAddress.fromString(TEST_DEFAULT_BSSID)))
4065                 .withScanResultInfo(info)
4066                 .withDisplayName(TEST_DEFAULT_SSID)
4067                 .withoutIPv4()
4068                 .build();
4069         setFeatureEnabled(NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION,
4070                 isMulticastResolicitEnabled);
4071         startIpClientProvisioning(config);
4072         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true);
4073         doIpv6OnlyProvisioning();
4074 
4075         // Simulate the roaming.
4076         forceLayer2Roaming();
4077     }
4078 
4079     private void runIpReachabilityMonitorProbeFailedTest() throws Exception {
4080         prepareIpReachabilityMonitorTest();
4081 
4082         final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations();
4083         final int expectedNudSolicitNum = readNudSolicitNumPostRoamingFromResource();
4084         assertEquals(expectedNudSolicitNum, nsList.size());
4085         for (NeighborSolicitation ns : nsList) {
4086             assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */,
4087                     ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
4088         }
4089     }
4090 
4091     @Test
4092     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
4093     public void testIpReachabilityMonitor_probeFailed() throws Exception {
4094         runIpReachabilityMonitorProbeFailedTest();
4095         assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
4096                 NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL);
4097     }
4098 
4099     @Test @SignatureRequiredTest(reason = "requires mock callback object")
4100     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
4101     public void testIpReachabilityMonitor_probeFailed_legacyCallback() throws Exception {
4102         when(mCb.getInterfaceVersion()).thenReturn(12 /* assign an older interface aidl version */);
4103 
4104         runIpReachabilityMonitorProbeFailedTest();
4105         verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any());
4106         verify(mCb, never()).onReachabilityFailure(any());
4107     }
4108 
4109     @Test
4110     public void testIpReachabilityMonitor_probeReachable() throws Exception {
4111         prepareIpReachabilityMonitorTest();
4112 
4113         final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */,
4114                 ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
4115 
4116         // Reply Neighbor Advertisement and check notifyLost callback won't be triggered.
4117         int flag = NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
4118         final ByteBuffer na = NeighborAdvertisement.build(ROUTER_MAC /* srcMac */,
4119                 ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */,
4120                 ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */);
4121         mPacketReader.sendResponse(na);
4122         assertNeverNotifyNeighborLost();
4123     }
4124 
4125     private void runIpReachabilityMonitorMcastResolicitProbeFailedTest() throws Exception {
4126         prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);
4127 
4128         final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations();
4129         final int expectedNudSolicitNum = readNudSolicitNumPostRoamingFromResource();
4130         int expectedSize = expectedNudSolicitNum + NUD_MCAST_RESOLICIT_NUM;
4131         assertEquals(expectedSize, nsList.size());
4132         for (NeighborSolicitation ns : nsList.subList(0, expectedNudSolicitNum)) {
4133             assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */,
4134                     ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
4135         }
4136         for (NeighborSolicitation ns : nsList.subList(expectedNudSolicitNum, nsList.size())) {
4137             assertMulticastNeighborSolicitation(ns, ROUTER_LINK_LOCAL /* targetIp */);
4138         }
4139     }
4140 
4141     @Test
4142     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
4143     public void testIpReachabilityMonitor_mcastResolicitProbeFailed() throws Exception {
4144         runIpReachabilityMonitorMcastResolicitProbeFailedTest();
4145         assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
4146                 NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL);
4147     }
4148 
4149     @Test @SignatureRequiredTest(reason = "requires mock callback object")
4150     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
4151     public void testIpReachabilityMonitor_mcastResolicitProbeFailed_legacyCallback()
4152             throws Exception {
4153         when(mCb.getInterfaceVersion()).thenReturn(12 /* assign an older interface aidl version */);
4154 
4155         runIpReachabilityMonitorMcastResolicitProbeFailedTest();
4156         verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any());
4157         verify(mCb, never()).onReachabilityFailure(any());
4158     }
4159 
4160     @Test
4161     public void testIpReachabilityMonitor_mcastResolicitProbeReachableWithSameLinkLayerAddress()
4162             throws Exception {
4163         prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);
4164 
4165         final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */,
4166                 ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
4167 
4168         // Reply Neighbor Advertisement and check notifyLost callback won't be triggered.
4169         int flag = NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
4170         final ByteBuffer na = NeighborAdvertisement.build(ROUTER_MAC /* srcMac */,
4171                 ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */,
4172                 ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */);
4173         mPacketReader.sendResponse(na);
4174         assertNeverNotifyNeighborLost();
4175     }
4176 
4177     @Test
4178     public void testIpReachabilityMonitor_mcastResolicitProbeReachableWithDiffLinkLayerAddress()
4179             throws Exception {
4180         prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);
4181 
4182         final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */,
4183                 ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
4184 
4185         // Reply Neighbor Advertisement with a different link-layer address and check notifyLost
4186         // callback will be triggered. Override flag must be set, which indicates that the
4187         // advertisement should override an existing cache entry and update the cached link-layer
4188         // address, otherwise, kernel won't transit to REACHABLE state with a different link-layer
4189         // address.
4190         int flag = NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED
4191                 | NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
4192         final MacAddress newMac = MacAddress.fromString("00:1a:11:22:33:55");
4193         final ByteBuffer na = NeighborAdvertisement.build(newMac /* srcMac */,
4194                 ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */,
4195                 ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */);
4196         mPacketReader.sendResponse(na);
4197         assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
4198                 NudEventType.NUD_POST_ROAMING_MAC_ADDRESS_CHANGED);
4199     }
4200 
4201     private void prepareIpReachabilityMonitorIpv4AddressResolutionTest() throws Exception {
4202         mNetworkAgentThread =
4203                 new HandlerThread(IpClientIntegrationTestCommon.class.getSimpleName());
4204         mNetworkAgentThread.start();
4205 
4206         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4207                 .withoutIPv6()
4208                 .build();
4209         setDhcpFeatures(true /* isRapidCommitEnabled */, false /* isDhcpIpConflictDetectEnabled */);
4210         startIpClientProvisioning(config);
4211 
4212         // Start IPv4 provisioning and wait until entire provisioning completes.
4213         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
4214                 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
4215         final LinkProperties lp =
4216                 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
4217 
4218         runAsShell(MANAGE_TEST_NETWORKS, () -> createTestNetworkAgentAndRegister(lp));
4219 
4220         // Send a UDP packet to IPv4 DNS server to trigger address resolution process for IPv4
4221         // on-link DNS server or default router.
4222         final Random random = new Random();
4223         final byte[] data = new byte[100];
4224         random.nextBytes(data);
4225         sendUdpPacketToNetwork(mNetworkAgent.getNetwork(), SERVER_ADDR, 1234 /* port */, data);
4226     }
4227 
4228     private void doTestIpReachabilityMonitor_replyBroadcastArpRequestWithDiffMacAddresses(
4229             boolean disconnect) throws Exception {
4230         prepareIpReachabilityMonitorIpv4AddressResolutionTest();
4231 
4232         // Respond to the broadcast ARP request.
4233         final ArpPacket request = getNextArpPacket();
4234         assertArpRequest(request, SERVER_ADDR);
4235         sendArpReply(request.senderHwAddress.toByteArray() /* dst */, ROUTER_MAC_BYTES /* srcMac */,
4236                 request.senderIp /* target IP */, SERVER_ADDR /* sender IP */);
4237 
4238         Thread.sleep(1500);
4239 
4240         // Reply with a different MAC address but the same server IP.
4241         final MacAddress gateway = MacAddress.fromString("00:11:22:33:44:55");
4242         sendArpReply(request.senderHwAddress.toByteArray() /* dst */,
4243                 gateway.toByteArray() /* srcMac */,
4244                 request.senderIp /* target IP */, SERVER_ADDR /* sender IP */);
4245 
4246         if (disconnect) {
4247             final ArgumentCaptor<ReachabilityLossInfoParcelable> lossInfoCaptor =
4248                     ArgumentCaptor.forClass(ReachabilityLossInfoParcelable.class);
4249             verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityFailure(lossInfoCaptor.capture());
4250             assertEquals(ReachabilityLossReason.ORGANIC, lossInfoCaptor.getValue().reason);
4251         } else {
4252             verify(mCb, after(100).never()).onReachabilityFailure(any());
4253         }
4254     }
4255 
4256     @Test
4257     public void testIpReachabilityMonitor_macAddressChangedWithoutRoam_ok()
4258             throws Exception {
4259         setFeatureChickenedOut(IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION,
4260                 false);
4261         doTestIpReachabilityMonitor_replyBroadcastArpRequestWithDiffMacAddresses(false);
4262     }
4263 
4264     @Test
4265     public void testIpReachabilityMonitor_macAddressChangedWithoutRoam_disconnect()
4266             throws Exception {
4267         setFeatureChickenedOut(IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION,
4268                 true);
4269         doTestIpReachabilityMonitor_replyBroadcastArpRequestWithDiffMacAddresses(true);
4270     }
4271 
4272     @Test
4273     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = true)
4274     public void testIpReachabilityMonitor_ignoreIpv4DefaultRouterOrganicNudFailure()
4275             throws Exception {
4276         prepareIpReachabilityMonitorIpv4AddressResolutionTest();
4277 
4278         ArpPacket packet;
4279         while ((packet = getNextArpPacket(TEST_TIMEOUT_MS)) != null) {
4280             // wait address resolution to complete.
4281         }
4282         verify(mCb, never()).onReachabilityFailure(any());
4283     }
4284 
4285     @Test
4286     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
4287     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = false)
4288     public void testIpReachabilityMonitor_ignoreIpv4DefaultRouterOrganicNudFailure_flagoff()
4289             throws Exception {
4290         prepareIpReachabilityMonitorIpv4AddressResolutionTest();
4291 
4292         ArpPacket packet;
4293         while ((packet = getNextArpPacket(TEST_TIMEOUT_MS)) != null) {
4294             // wait address resolution to complete.
4295         }
4296         final ArgumentCaptor<ReachabilityLossInfoParcelable> lossInfoCaptor =
4297                 ArgumentCaptor.forClass(ReachabilityLossInfoParcelable.class);
4298         verify(mCb).onReachabilityFailure(lossInfoCaptor.capture());
4299         assertEquals(ReachabilityLossReason.ORGANIC, lossInfoCaptor.getValue().reason);
4300     }
4301 
4302     private void sendUdpPacketToNetwork(final Network network, final InetAddress remoteIp,
4303             int port, final byte[] data) throws Exception {
4304         final InetAddress laddr =
4305                 (remoteIp instanceof Inet6Address) ? Inet6Address.ANY : Inet4Address.ANY;
4306         final DatagramSocket socket = new DatagramSocket(0, laddr);
4307         final DatagramPacket pkt = new DatagramPacket(data, data.length, remoteIp, port);
4308         network.bindSocket(socket);
4309         socket.send(pkt);
4310     }
4311 
4312     private void prepareIpReachabilityMonitorAddressResolutionTest(final String dnsServer,
4313             final Inet6Address targetIp) throws Exception {
4314         mNetworkAgentThread =
4315                 new HandlerThread(IpClientIntegrationTestCommon.class.getSimpleName());
4316         mNetworkAgentThread.start();
4317 
4318         setDhcpFeatures(true /* isRapidCommitEnabled */,
4319                 false /* isDhcpIpConflictDetectEnabled */);
4320         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4321                 // We've found that mCm.shouldAvoidBadWifi() has a flaky behavior in the root test,
4322                 // probably due to the sim card in the DUT. it doesn't occur in the siganture test
4323                 // since we mock the return value directly. As a result, sometimes
4324                 // IpReachabilityMonitor#avoidingBadLinks() returns false, it caused the expected
4325                 // onReachabilityFailure callback wasn't triggered on the test. In order to make
4326                 // the root test more stable, do not use MultinetworkPolicyTracker only for IPv6
4327                 // neighbor reachability checking relevant test cases, that guarantees
4328                 // avoidingBadLinks() always returns true which is expected.
4329                 .withoutMultinetworkPolicyTracker()
4330                 // Make cluster as non-null to test the NUD failure event count query logic.
4331                 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
4332                        MacAddress.fromString(TEST_DEFAULT_BSSID)))
4333                 .build();
4334         startIpClientProvisioning(config);
4335         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true);
4336 
4337         final List<ByteBuffer> options = new ArrayList<ByteBuffer>();
4338         options.add(buildPioOption(3600, 1800, "2001:db8:1::/64")); // PIO
4339         options.add(buildRdnssOption(3600, dnsServer));             // RDNSS
4340         // If target IP of address resolution is default router's IPv6 link-local address,
4341         // then we should not take SLLA option in RA.
4342         if (!targetIp.equals(ROUTER_LINK_LOCAL)) {
4343             options.add(buildSllaOption());                         // SLLA
4344         }
4345         final ByteBuffer ra = buildRaPacket(options.toArray(new ByteBuffer[options.size()]));
4346         final Inet6Address dnsServerIp = ipv6Addr(dnsServer);
4347         final LinkProperties lp = performDualStackProvisioning(ra, dnsServerIp);
4348         runAsShell(MANAGE_TEST_NETWORKS, () -> createTestNetworkAgentAndRegister(lp));
4349     }
4350 
4351     /**
4352      * Send a UDP packet to dstIp to trigger address resolution for targetIp, and possibly expect a
4353      * neighbor lost callback.
4354      * If dstIp is on-link, then dstIp and targetIp should be the same.
4355      * If dstIp is off-link, then targetIp should be the IPv6 default router.
4356      * The ND cache should not have an entry for targetIp.
4357      */
4358     private void sendPacketToUnreachableNeighbor(Inet6Address dstIp) throws Exception {
4359         final Random random = new Random();
4360         final byte[] data = new byte[100];
4361         random.nextBytes(data);
4362         sendUdpPacketToNetwork(mNetworkAgent.getNetwork(), dstIp, 1234 /* port */, data);
4363     }
4364 
4365     private void expectAndDropMulticastNses(Inet6Address targetIp, boolean expectNeighborLost)
4366             throws Exception {
4367         // Wait for the multicast NSes but never respond to them, that results in the on-link
4368         // DNS gets lost and onReachabilityLost callback will be invoked.
4369         final List<NeighborSolicitation> nsList = new ArrayList<NeighborSolicitation>();
4370         NeighborSolicitation ns;
4371         while ((ns = getNextNeighborSolicitation()) != null) {
4372             // multicast NS for address resolution, IPv6 dst address in that NS is solicited-node
4373             // multicast address based on the target IP, the target IP is either on-link IPv6 DNS
4374             // server address or IPv6 link-local address of default gateway.
4375             final LinkAddress actual = new LinkAddress(ns.nsHdr.target, 64);
4376             final LinkAddress target = new LinkAddress(targetIp, 64);
4377             if (actual.equals(target) && ns.ipv6Hdr.dstIp.isMulticastAddress()) {
4378                 nsList.add(ns);
4379             }
4380         }
4381         assertFalse(nsList.isEmpty());
4382 
4383         if (expectNeighborLost) {
4384             assertNotifyNeighborLost(targetIp, NudEventType.NUD_ORGANIC_FAILED_CRITICAL);
4385         } else {
4386             assertNeverNotifyNeighborLost();
4387         }
4388     }
4389 
4390     private void runIpReachabilityMonitorAddressResolutionTest(final String dnsServer,
4391             final Inet6Address targetIp,
4392             final boolean expectNeighborLost) throws Exception {
4393         prepareIpReachabilityMonitorAddressResolutionTest(dnsServer, targetIp);
4394         sendPacketToUnreachableNeighbor(ipv6Addr(dnsServer));
4395         expectAndDropMulticastNses(targetIp, expectNeighborLost);
4396     }
4397 
4398     @Test
4399     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = true)
4400     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = false)
4401     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = false)
4402     public void testIpReachabilityMonitor_incompleteIpv6DnsServerInDualStack() throws Exception {
4403         final Inet6Address targetIp = ipv6Addr(IPV6_ON_LINK_DNS_SERVER);
4404         runIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER, targetIp,
4405                 false /* expectNeighborLost */);
4406     }
4407 
4408     @Test
4409     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = false)
4410     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = false)
4411     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = false)
4412     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
4413     public void testIpReachabilityMonitor_incompleteIpv6DnsServerInDualStack_flagoff()
4414             throws Exception {
4415         final Inet6Address targetIp = ipv6Addr(IPV6_ON_LINK_DNS_SERVER);
4416         runIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER, targetIp,
4417                 true /* expectNeighborLost */);
4418     }
4419 
4420     @Test
4421     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = false)
4422     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = true)
4423     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = false)
4424     public void testIpReachabilityMonitor_incompleteIpv6DefaultRouterInDualStack()
4425             throws Exception {
4426         runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
4427                 ROUTER_LINK_LOCAL /* targetIp */,
4428                 false /* expectNeighborLost */);
4429     }
4430 
4431     @Test
4432     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = false)
4433     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = false)
4434     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = false)
4435     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
4436     public void testIpReachabilityMonitor_incompleteIpv6DefaultRouterInDualStack_flagoff()
4437             throws Exception {
4438         runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
4439                 ROUTER_LINK_LOCAL /* targetIp */,
4440                 true /* expectNeighborLost */);
4441     }
4442 
4443     @Test
4444     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = false)
4445     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = false)
4446     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = true)
4447     public void testIpReachabilityMonitor_ignoreOnLinkIpv6DnsOrganicNudFailure()
4448             throws Exception {
4449         final Inet6Address targetIp = ipv6Addr(IPV6_ON_LINK_DNS_SERVER);
4450         runIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER, targetIp,
4451                 false /* expectNeighborLost */);
4452     }
4453 
4454     @Test
4455     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = false)
4456     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = false)
4457     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = false)
4458     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
4459     public void testIpReachabilityMonitor_ignoreOnLinkIpv6DnsOrganicNudFailure_flagoff()
4460             throws Exception {
4461         final Inet6Address targetIp = ipv6Addr(IPV6_ON_LINK_DNS_SERVER);
4462         runIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER, targetIp,
4463                 true /* expectNeighborLost */);
4464     }
4465 
4466     @Test
4467     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = false)
4468     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = false)
4469     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = true)
4470     public void testIpReachabilityMonitor_ignoreIpv6DefaultRouterOrganicNudFailure()
4471             throws Exception {
4472         runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
4473                 ROUTER_LINK_LOCAL /* targetIp */,
4474                 false /* expectNeighborLost */);
4475     }
4476 
4477     @Test
4478     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = false)
4479     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = false)
4480     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = false)
4481     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
4482     public void testIpReachabilityMonitor_ignoreIpv6DefaultRouterOrganicNudFailure_flagoff()
4483             throws Exception {
4484         runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
4485                 ROUTER_LINK_LOCAL /* targetIp */,
4486                 true /* expectNeighborLost */);
4487     }
4488 
4489     private void runIpReachabilityMonitorEverReachableIpv6NeighborTest(final String dnsServer,
4490             final Inet6Address targetIp) throws Exception {
4491         prepareIpReachabilityMonitorAddressResolutionTest(dnsServer, targetIp);
4492         sendPacketToUnreachableNeighbor(ipv6Addr(dnsServer));
4493 
4494         // Simulate the default router/DNS was reachable by responding to multicast NS(not for DAD).
4495         NeighborSolicitation ns;
4496         while ((ns = getNextNeighborSolicitation()) != null) {
4497             if (ns.ipv6Hdr.dstIp.isMulticastAddress() // Solicited-node multicast address
4498                     && ns.nsHdr.target.equals(targetIp)) {
4499                 final ByteBuffer na = NeighborAdvertisement.build(ROUTER_MAC /* srcMac */,
4500                         ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */,
4501                         ns.ipv6Hdr.srcIp /* dstIp */,
4502                         NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED,
4503                         targetIp);
4504                 mPacketReader.sendResponse(na);
4505                 break;
4506             }
4507         }
4508 
4509         // Trigger the NUD probe manually by sending CMD_CONFIRM command, this will force to start
4510         // probing for all neighbors in the watchlist including default router and on-link DNS.
4511         mIIpClient.confirmConfiguration();
4512 
4513         // Wait for the next unicast NS probes, but don't respond to them, which should trigger
4514         // reachability failure callback because the probe status is from probed to failed, rather
4515         // than incomplete to failed.
4516         while ((ns = getNextNeighborSolicitation()) != null) {
4517             // Respond to NS for default router, it's used to avoid triggering multiple
4518             // onReachabilityFailure callbacks.
4519             if (!targetIp.equals(ROUTER_LINK_LOCAL)) {
4520                 final ByteBuffer na = NeighborAdvertisement.build(ROUTER_MAC /* srcMac */,
4521                         ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */,
4522                         ns.ipv6Hdr.srcIp /* dstIp */,
4523                         NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED,
4524                         ROUTER_LINK_LOCAL);
4525                 mPacketReader.sendResponse(na);
4526             }
4527         }
4528         assertNotifyNeighborLost(targetIp, NudEventType.NUD_CONFIRM_FAILED_CRITICAL);
4529     }
4530 
4531     @Test
4532     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = false)
4533     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = true)
4534     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = false)
4535     public void testIpReachabilityMonitor_ignoreIpv6DefaultRouter_everReachable() throws Exception {
4536         runIpReachabilityMonitorEverReachableIpv6NeighborTest(IPV6_OFF_LINK_DNS_SERVER,
4537                 ROUTER_LINK_LOCAL /* targetIp */);
4538     }
4539 
4540     @Test
4541     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = true)
4542     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = false)
4543     @Flag(name = IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION, enabled = false)
4544     public void testIpReachabilityMonitor_ignoreIpv6Dns_everReachable() throws Exception {
4545         runIpReachabilityMonitorEverReachableIpv6NeighborTest(IPV6_ON_LINK_DNS_SERVER,
4546                 ipv6Addr(IPV6_ON_LINK_DNS_SERVER) /* targetIp */);
4547     }
4548 
4549     @Test
4550     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = true)
4551     public void testIpReachabilityMonitor_ignoreNeverReachableIpv6Dns() throws Exception {
4552         runIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER,
4553                 ipv6Addr(IPV6_ON_LINK_DNS_SERVER), false /* expectNeighborLost */);
4554     }
4555 
4556     @Test
4557     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = true)
4558     public void testIpReachabilityMonitor_ignoreNeverReachableIpv6Dns_butEverReachable()
4559             throws Exception {
4560         runIpReachabilityMonitorEverReachableIpv6NeighborTest(IPV6_ON_LINK_DNS_SERVER,
4561                 ipv6Addr(IPV6_ON_LINK_DNS_SERVER) /* targetIp */);
4562     }
4563 
4564     @Test
4565     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = true)
4566     public void testIpReachabilityMonitor_ignoreNeverReachableIpv6DefaultRouter() throws Exception {
4567         runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
4568                 ROUTER_LINK_LOCAL, false /* expectNeighborLost */);
4569     }
4570 
4571     @Test
4572     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = true)
4573     public void testIpReachabilityMonitor_ignoreNeverReachableIpv6DefaultRouter_butEverReachable()
4574             throws Exception {
4575         runIpReachabilityMonitorEverReachableIpv6NeighborTest(IPV6_ON_LINK_DNS_SERVER,
4576                 ROUTER_LINK_LOCAL /* targetIp */);
4577     }
4578 
4579     @Test
4580     public void testIPv6LinkLocalOnly() throws Exception {
4581         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4582                 .withoutIPv4()
4583                 .withIpv6LinkLocalOnly()
4584                 .withRandomMacAddress()
4585                 .build();
4586         startIpClientProvisioning(config);
4587 
4588         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
4589         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
4590         final LinkProperties lp = captor.getValue();
4591         assertNotNull(lp);
4592         assertEquals(0, lp.getDnsServers().size());
4593         final List<LinkAddress> addresses = lp.getLinkAddresses();
4594         assertEquals(1, addresses.size());
4595         assertTrue(addresses.get(0).getAddress().isLinkLocalAddress()); // only IPv6 link-local
4596         assertTrue(hasRouteTo(lp, IPV6_LINK_LOCAL_PREFIX)); // fe80::/64 -> :: iface mtu 0
4597 
4598         // Check that if an RA is received, no IP addresses, routes, or DNS servers are configured.
4599         // Instead of waiting some period of time for the RA to be received and checking the
4600         // LinkProperties after that, tear down the interface and wait for it to go down. Then check
4601         // that no LinkProperties updates ever contained non-link-local information.
4602         sendBasicRouterAdvertisement(false /* waitForRs */);
4603         teardownTapInterface();
4604         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
4605         verify(mCb, never()).onLinkPropertiesChange(argThat(newLp ->
4606                 // Ideally there should be only one route(fe80::/64 -> :: iface mtu 0) in the
4607                 // LinkProperties, however, the multicast route(ff00::/8 -> :: iface mtu 0) may
4608                 // appear on some old platforms where the kernel is still notifying the userspace
4609                 // the multicast route. Therefore, we cannot assert that size of routes in the
4610                 // LinkProperties is more than one, but other properties such as DNS or IPv6
4611                 // default route or global IPv6 address should never appear in the IPv6 link-local
4612                 // only mode.
4613                 newLp.getDnsServers().size() != 0
4614                         || newLp.hasIpv6DefaultRoute()
4615                         || newLp.hasGlobalIpv6Address()
4616         ));
4617     }
4618 
4619     @Test
4620     public void testIPv6LinkLocalOnly_verifyAcceptRaDefrtr() throws Exception {
4621         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4622                 .withoutIPv4()
4623                 .withIpv6LinkLocalOnly()
4624                 .withRandomMacAddress()
4625                 .build();
4626         startIpClientProvisioning(config);
4627         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any());
4628 
4629         clearInvocations(mCb);
4630 
4631         // accept_ra is set to 0 and accept_ra_defrtr is set to 1 in IPv6 link-local only mode,
4632         // send another RA to tap interface, to verify that we should not see any IPv6 provisioning
4633         // although accept_ra_defrtr is set to 1.
4634         sendBasicRouterAdvertisement(false /* waitForRs */);
4635         verify(mCb, never()).onLinkPropertiesChange(argThat(x -> x.isIpv6Provisioned()));
4636     }
4637 
4638     @Test
4639     public void testIPv6LinkLocalOnlyAndThenGlobal() throws Exception {
4640         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4641                 .withoutIPv4()
4642                 .withIpv6LinkLocalOnly()
4643                 .withRandomMacAddress()
4644                 .build();
4645         startIpClientProvisioning(config);
4646         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any());
4647         mIIpClient.stop();
4648         verifyAfterIpClientShutdown();
4649         reset(mCb);
4650 
4651         // Speed up provisioning by enabling rapid commit. TODO: why is this necessary?
4652         setDhcpFeatures(true /* isRapidCommitEnabled */,
4653                 false /* isDhcpIpConflictDetectEnabled */);
4654         config = new ProvisioningConfiguration.Builder()
4655                 .build();
4656         startIpClientProvisioning(config);
4657         performDualStackProvisioning();
4658         // No exceptions? Dual-stack provisioning worked.
4659     }
4660 
4661     @Test
4662     public void testIPv6LinkLocalOnly_enableBothIPv4andIPv6LinkLocalOnly() throws Exception {
4663         assertThrows(IllegalArgumentException.class,
4664                 () -> new ProvisioningConfiguration.Builder()
4665                         .withoutIpReachabilityMonitor()
4666                         .withIpv6LinkLocalOnly()
4667                         .withRandomMacAddress()
4668                         .build()
4669         );
4670     }
4671 
4672     private void runIpv6LinkLocalOnlyDadTransmitsCheckTest(boolean shouldDisableDad)
4673             throws Exception {
4674         ProvisioningConfiguration.Builder config = new ProvisioningConfiguration.Builder()
4675                 .withoutIPv4()
4676                 .withIpv6LinkLocalOnly()
4677                 .withRandomMacAddress();
4678         if (shouldDisableDad) config.withUniqueEui64AddressesOnly();
4679 
4680         // dad_transmits has been set to 0 in disableIpv6ProvisioningDelays, re-enable dad_transmits
4681         // for testing, but production code could disable dad again later, we should never see any
4682         // multicast NS for duplicate address detection then.
4683         mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits", "1");
4684         startIpClientProvisioning(config.build());
4685         verify(mNetd, timeout(TEST_TIMEOUT_MS)).interfaceSetEnableIPv6(mIfaceName, true);
4686         // Check dad_transmits should be set to 0 if UniqueEui64AddressesOnly mode is enabled.
4687         int dadTransmits = Integer.parseUnsignedInt(
4688                 mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits"));
4689         if (shouldDisableDad) {
4690             assertEquals(0, dadTransmits);
4691         } else {
4692             assertEquals(1, dadTransmits);
4693         }
4694 
4695         final NeighborSolicitation ns =
4696                 expectDadNeighborSolicitationForLinkLocal(shouldDisableDad);
4697         if (shouldDisableDad) {
4698             assertNull(ns);
4699         } else {
4700             assertNotNull(ns);
4701         }
4702 
4703         // Shutdown IpClient and check if the dad_transmits always equals to default value 1 (if
4704         // dad_transmit was set to 0 before, it should get recovered to default value 1 after
4705         // shutting down IpClient)
4706         mIpc.shutdown();
4707         awaitIpClientShutdown();
4708         dadTransmits = Integer.parseUnsignedInt(
4709                 mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits"));
4710         assertEquals(1, dadTransmits);
4711     }
4712 
4713     @Test
4714     @SignatureRequiredTest(reason = "requires mocked netd")
4715     public void testIPv6LinkLocalOnly_enableDad() throws Exception {
4716         runIpv6LinkLocalOnlyDadTransmitsCheckTest(false /* shouldDisableDad */);
4717     }
4718 
4719     @Test
4720     @SignatureRequiredTest(reason = "requires mocked netd")
4721     public void testIPv6LinkLocalOnly_disableDad() throws Exception {
4722         runIpv6LinkLocalOnlyDadTransmitsCheckTest(true /* shouldDisableDad */);
4723     }
4724 
4725     // Since createTapInterface(boolean, String) method was introduced since T, this method
4726     // cannot be found on Q/R/S platform, ignore this test on T- platform.
4727     @Test
4728     @IgnoreUpTo(Build.VERSION_CODES.S_V2)
4729     public void testIpClientLinkObserver_onClatInterfaceStateUpdate() throws Exception {
4730         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4731                 .withoutIPv4()
4732                 .build();
4733         startIpClientProvisioning(config);
4734         doIpv6OnlyProvisioning();
4735 
4736         reset(mCb);
4737 
4738         // Add the clat interface and check the callback.
4739         final TestNetworkInterface clatIface = setUpClatInterface(mIfaceName);
4740         assertNotNull(clatIface);
4741         assertTrue(clatIface.getInterfaceName().equals(CLAT_PREFIX + mIfaceName));
4742         verify(mCb, timeout(TEST_TIMEOUT_MS)).setNeighborDiscoveryOffload(false);
4743 
4744         // Remove the clat interface and check the callback.
4745         removeTestInterface(clatIface.getFileDescriptor().getFileDescriptor());
4746         verify(mCb, timeout(TEST_TIMEOUT_MS)).setNeighborDiscoveryOffload(true);
4747     }
4748 
4749     @Test @SignatureRequiredTest(reason = "requires mock callback object")
4750     public void testNetlinkSocketReceiveENOBUFS() throws Exception {
4751         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4752                 .withoutIPv4()
4753                 .build();
4754         startIpClientProvisioning(config);
4755         doIpv6OnlyProvisioning();
4756         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
4757 
4758         final Handler handler = mIpc.getHandler();
4759         // Block IpClient handler.
4760         final CountDownLatch latch = new CountDownLatch(1);
4761         handler.post(() -> {
4762             try {
4763                 latch.await(10, TimeUnit.SECONDS);
4764             } catch (InterruptedException e) {
4765                 fail("latch wait unexpectedly interrupted");
4766             }
4767         });
4768 
4769         // Send large amount of RAs to overflow the netlink socket receive buffer.
4770         for (int i = 0; i < 200; i++) {
4771             sendBasicRouterAdvertisement(false /* waitRs */);
4772         }
4773 
4774         // Send another RA with a different IPv6 global prefix. This PIO option should be dropped
4775         // due to the ENOBUFS happens, it means IpClient shouldn't see the new IPv6 global prefix.
4776         final String prefix = "2001:db8:dead:beef::/64";
4777         final ByteBuffer pio = buildPioOption(3600, 1800, prefix);
4778         ByteBuffer rdnss = buildRdnssOption(3600, IPV6_OFF_LINK_DNS_SERVER);
4779         sendRouterAdvertisement(false /* waitForRs */, (short) 1800, pio, rdnss);
4780 
4781         // Unblock the IpClient handler and ENOBUFS should happen then.
4782         latch.countDown();
4783         HandlerUtils.waitForIdle(handler, TEST_WAIT_ENOBUFS_TIMEOUT_MS);
4784 
4785         reset(mCb);
4786 
4787         // Send RA with 0 router lifetime to see if IpClient can see the loss of IPv6 default route.
4788         // Due to ignoring the ENOBUFS and wait until handler gets idle, IpClient should be still
4789         // able to see the RA with 0 router lifetime and the IPv6 default route will be removed.
4790         // LinkProperties should not include any route to the new prefix 2001:db8:dead:beef::/64.
4791         sendRouterAdvertisementWithZeroRouterLifetime();
4792         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
4793         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
4794         final LinkProperties lp = captor.getValue();
4795         assertNotNull(lp);
4796         assertFalse(hasRouteTo(lp, prefix));
4797         assertFalse(lp.hasIpv6DefaultRoute());
4798     }
4799 
4800     @Test
4801     public void testMulticastNsFromIPv6Gua() throws Exception {
4802         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4803                 .withoutIpReachabilityMonitor()
4804                 .withoutIPv4()
4805                 .build();
4806 
4807         startIpClientProvisioning(config);
4808 
4809         doIpv6OnlyProvisioning();
4810 
4811         final List<NeighborSolicitation> nsList = new ArrayList<>();
4812         NeighborSolicitation packet;
4813         while ((packet = getNextNeighborSolicitation()) != null) {
4814             // Filter out the NSes used for duplicate address detetction, whose target address
4815             // is the global IPv6 address inside these NSes.
4816             if (packet.nsHdr.target.isLinkLocalAddress()) {
4817                 assertMulticastNsFromIpv6Gua(packet);
4818                 nsList.add(packet);
4819             }
4820         }
4821         assertEquals(2, nsList.size()); // from privacy address and stable privacy address
4822     }
4823 
4824     @Test
4825     public void testDeprecatedGlobalUnicastAddress() throws Exception {
4826         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4827                 .withoutIPv4()
4828                 .build();
4829         startIpClientProvisioning(config);
4830         doIpv6OnlyProvisioning();
4831 
4832         // Send RA with PIO(0 preferred but valid lifetime) to deprecate the global IPv6 addresses.
4833         // Check all of global IPv6 addresses will become deprecated, but still valid.
4834         // NetworkStackUtils#isIPv6GUA() will return false for deprecated addresses, however, when
4835         // checking if the DNS is still reachable, deprecated addresses are not acceptable, that
4836         // results in the on-link DNS server gets lost from LinkProperties, and provisioning failure
4837         // happened.
4838         // TODO: update the logic of checking reachable on-link DNS server to accept the deprecated
4839         // addresses, then onProvisioningFailure callback should never happen.
4840         sendRouterAdvertisement(false /* waitForRs*/, (short) 1800 /* router lifetime */,
4841                 3600 /* valid */, 0 /* preferred */);
4842         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
4843         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
4844         final LinkProperties lp = captor.getValue();
4845         assertNotNull(lp);
4846         assertFalse(lp.hasGlobalIpv6Address());
4847         assertEquals(3, lp.getLinkAddresses().size()); // IPv6 privacy, stable privacy, link-local
4848         for (LinkAddress la : lp.getLinkAddresses()) {
4849             assertFalse(NetworkStackUtils.isIPv6GUA(la));
4850         }
4851     }
4852 
4853     @Test @SignatureRequiredTest(reason = "requires mNetd to delete IPv6 GUAs")
4854     public void testOnIpv6AddressRemoved() throws Exception {
4855         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4856                 .withoutIPv4()
4857                 .build();
4858         startIpClientProvisioning(config);
4859 
4860         LinkProperties lp = doIpv6OnlyProvisioning();
4861         assertNotNull(lp);
4862         assertEquals(3, lp.getLinkAddresses().size()); // IPv6 privacy, stable privacy, link-local
4863         for (LinkAddress la : lp.getLinkAddresses()) {
4864             final Inet6Address address = (Inet6Address) la.getAddress();
4865             if (address.isLinkLocalAddress()) continue;
4866             // Remove IPv6 GUAs from interface.
4867             mNetd.interfaceDelAddress(mIfaceName, address.getHostAddress(), la.getPrefixLength());
4868         }
4869 
4870         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
4871         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
4872         lp = captor.getValue();
4873         assertFalse(lp.hasGlobalIpv6Address());
4874         assertEquals(1, lp.getLinkAddresses().size()); // only link-local
4875     }
4876 
4877     @Test
4878     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
4879     public void testMaxDtimMultiplier_IPv6OnlyNetwork() throws Exception {
4880         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4881                 .withoutIPv4()
4882                 .build();
4883         startIpClientProvisioning(config);
4884 
4885         verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
4886                 IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
4887 
4888         LinkProperties lp = doIpv6OnlyProvisioning();
4889         assertNotNull(lp);
4890         assertEquals(3, lp.getLinkAddresses().size()); // IPv6 privacy, stable privacy, link-local
4891         verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
4892                 IpClient.DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
4893     }
4894 
4895     @Test
4896     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
4897     public void testMaxDtimMultiplier_IPv6LinkLocalOnlyMode() throws Exception {
4898         final InOrder inOrder = inOrder(mCb);
4899         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
4900                 .withoutIPv4()
4901                 .withIpv6LinkLocalOnly()
4902                 .build();
4903         startIpClientProvisioning(config);
4904         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any());
4905         // IPv6 DTIM grace period doesn't apply to IPv6 link-local only mode and the multiplier
4906         // has been initialized to DTIM_MULTIPLIER_RESET before starting provisioning, therefore,
4907         // the multiplier should not be updated neither.
4908         verify(mCb, never()).setMaxDtimMultiplier(
4909                 IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
4910         verify(mCb, never()).setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET);
4911     }
4912 
4913     @Test
4914     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
4915     public void testMaxDtimMultiplier_IPv4OnlyNetwork() throws Exception {
4916         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
4917                 false /* shouldReplyRapidCommitAck */,
4918                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
4919         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
4920         verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setMaxDtimMultiplier(
4921                 IpClient.DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER);
4922         // IPv6 DTIM grace period doesn't apply to IPv4-only networks.
4923         verify(mCb, never()).setMaxDtimMultiplier(
4924                 IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
4925     }
4926 
4927     private void runDualStackNetworkDtimMultiplierSetting(final InOrder inOrder) throws Exception {
4928         doDualStackProvisioning();
4929         inOrder.verify(mCb).setMaxDtimMultiplier(
4930                 IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER);
4931         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
4932                 IpClient.DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER);
4933     }
4934 
4935     @Test
4936     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
4937     public void testMaxDtimMultiplier_DualStackNetwork() throws Exception {
4938         final InOrder inOrder = inOrder(mCb);
4939         runDualStackNetworkDtimMultiplierSetting(inOrder);
4940     }
4941 
4942     @Test
4943     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
4944     public void testMaxDtimMultiplier_MulticastLock() throws Exception {
4945         final InOrder inOrder = inOrder(mCb);
4946         runDualStackNetworkDtimMultiplierSetting(inOrder);
4947 
4948         // Simulate to hold the multicast lock by disabling the multicast filter.
4949         mIIpClient.setMulticastFilter(false);
4950         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
4951                 IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER);
4952 
4953         // Simulate to disable the multicast lock again, then check the multiplier should be
4954         // changed to 2 (dual-stack setting)
4955         mIIpClient.setMulticastFilter(true);
4956         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(
4957                 IpClient.DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER);
4958     }
4959 
4960     @Test
4961     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
4962     public void testMaxDtimMultiplier_MulticastLockEnabled_StoppedState() throws Exception {
4963         // Simulate to hold the multicast lock by disabling the multicast filter at StoppedState,
4964         // verify no callback to be sent, start dual-stack provisioning and verify the multiplier
4965         // to be set to 1 (multicast lock setting) later.
4966         mIIpClient.setMulticastFilter(false);
4967         verify(mCb, after(10).never()).setMaxDtimMultiplier(
4968                 IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER);
4969 
4970         doDualStackProvisioning();
4971         verify(mCb, times(1)).setMaxDtimMultiplier(
4972                 IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER);
4973     }
4974 
4975     @Test
4976     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
4977     public void testMaxDtimMultiplier_resetMultiplier() throws Exception {
4978         final InOrder inOrder = inOrder(mCb);
4979         runDualStackNetworkDtimMultiplierSetting(inOrder);
4980 
4981         verify(mCb, never()).setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET);
4982 
4983         // Stop IpClient and verify if the multiplier has been reset.
4984         mIIpClient.stop();
4985         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET);
4986     }
4987 
4988     private IaPrefixOption buildIaPrefixOption(final IpPrefix prefix, int preferred,
4989             int valid) {
4990         return new IaPrefixOption((short) IaPrefixOption.LENGTH, preferred, valid,
4991                 (byte) prefix.getPrefixLength(), prefix.getRawAddress() /* prefix */);
4992     }
4993 
4994     private void handleDhcp6Packets(final IpPrefix prefix, boolean shouldReplyRapidCommit)
4995             throws Exception {
4996         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 4500 /* preferred */,
4997                 7200 /* valid */);
4998         handleDhcp6Packets(Collections.singletonList(ipo), 3600 /* t1 */, 4500 /* t2 */,
4999                 shouldReplyRapidCommit);
5000     }
5001 
5002     private void handleDhcp6Packets(final List<IaPrefixOption> ipos, int t1, int t2,
5003             boolean shouldReplyRapidCommit) throws Exception {
5004         ByteBuffer iapd;
5005         Dhcp6Packet packet;
5006         while ((packet = getNextDhcp6Packet()) != null) {
5007             final PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), t1, t2, ipos);
5008             iapd = pd.build();
5009             if (packet instanceof Dhcp6SolicitPacket) {
5010                 if (shouldReplyRapidCommit) {
5011                     mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5012                             (Inet6Address) mClientIpAddress, true /* rapidCommit */));
5013                 } else {
5014                     mPacketReader.sendResponse(buildDhcp6Advertise(packet, iapd.array(), mClientMac,
5015                             (Inet6Address) mClientIpAddress));
5016                 }
5017             } else if (packet instanceof Dhcp6RequestPacket) {
5018                 mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5019                           (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5020             } else {
5021                 fail("invalid DHCPv6 Packet");
5022             }
5023 
5024             if ((packet instanceof Dhcp6RequestPacket) || shouldReplyRapidCommit) {
5025                 return;
5026             }
5027         }
5028         fail("No DHCPv6 packet received on interface within timeout");
5029     }
5030 
5031     private void prepareDhcp6PdTest() throws Exception {
5032         final String dnsServer = "2001:4860:4860::64";
5033         final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer);
5034         final ByteBuffer ra = buildRaPacket(rdnss);
5035 
5036         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
5037                 .withoutIPv4()
5038                 .build();
5039         startIpClientProvisioning(config);
5040 
5041         waitForRouterSolicitation();
5042         mPacketReader.sendResponse(ra);
5043     }
5044 
5045     @Test
5046     @Flag(name =  IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION, enabled = true)
5047     public void testDhcp6Pd() throws Exception {
5048         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5049         prepareDhcp6PdTest();
5050         handleDhcp6Packets(prefix, true /* shouldReplyRapidCommit */);
5051         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5052         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
5053         final LinkProperties lp = captor.getValue();
5054         assertTrue(hasIpv6AddressPrefixedWith(lp, prefix));
5055 
5056         final long now = SystemClock.elapsedRealtime();
5057         long when = 0;
5058         for (LinkAddress la : lp.getLinkAddresses()) {
5059             if (la.getAddress().isLinkLocalAddress()) {
5060                 assertLinkAddressPermanentLifetime(la);
5061             } else if (la.isGlobalPreferred()) {
5062                 when = now + 4500 * 1000; // preferred=4500s
5063                 assertLinkAddressDeprecationTime(la, when);
5064                 when = now + 7200 * 1000; // valid=7200s
5065                 assertLinkAddressExpirationTime(la, when);
5066             }
5067         }
5068     }
5069 
5070     @Test
5071     public void testDhcp6Pd_disableRapidCommit() throws Exception {
5072         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5073         prepareDhcp6PdTest();
5074         handleDhcp6Packets(prefix, false /* shouldReplyRapidCommit */);
5075         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5076         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
5077         assertTrue(hasIpv6AddressPrefixedWith(captor.getValue(), prefix));
5078     }
5079 
5080     @Test
5081     public void testDhcp6Pd_longPrefixLength() throws Exception {
5082         prepareDhcp6PdTest();
5083         final IpPrefix prefix = new IpPrefix("2001:db8:1::/80");
5084         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 3600 /* preferred */,
5085                 4000 /* valid */);
5086         handleDhcp6Packets(Collections.singletonList(ipo), 3600 /* t1 */, 4500 /* t2 */,
5087                 true /* shouldReplyRapidCommit */);
5088         verify(mCb, never()).onProvisioningSuccess(any());
5089     }
5090 
5091     @Test
5092     public void testDhcp6Pd_shortPrefixLength() throws Exception {
5093         final IpPrefix prefix = new IpPrefix("2001:db8:1::/56");
5094         prepareDhcp6PdTest();
5095         handleDhcp6Packets(prefix, true /* shouldReplyRapidCommit */);
5096         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5097         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
5098         assertTrue(hasIpv6AddressPrefixedWith(captor.getValue(), prefix));
5099     }
5100 
5101     @Test
5102     public void testDhcp6Pd_T1GreaterThanT2() throws Exception {
5103         prepareDhcp6PdTest();
5104         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5105         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 3600 /* preferred */,
5106                 4000 /* valid */);
5107         handleDhcp6Packets(Collections.singletonList(ipo), 4500 /* t1 */, 3600 /* t2 */,
5108                 true /* shouldReplyRapidCommit */);
5109         verify(mCb, never()).onProvisioningSuccess(any());
5110     }
5111 
5112     @Test
5113     public void testDhcp6Pd_preferredLifetimeGreaterThanValidLifetime() throws Exception {
5114         prepareDhcp6PdTest();
5115         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5116         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 7200 /* preferred */,
5117                 4500 /* valid */);
5118         handleDhcp6Packets(Collections.singletonList(ipo), 3600 /* t1 */, 4500 /* t2 */,
5119                 true /* shouldReplyRapidCommit */);
5120         verify(mCb, never()).onProvisioningSuccess(any());
5121     }
5122 
5123     @Test
5124     public void testDhcp6Pd_preferredLifetimeLessThanT2() throws Exception {
5125         prepareDhcp6PdTest();
5126         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5127         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 3600 /* preferred */,
5128                 4000 /* valid */);
5129         handleDhcp6Packets(Collections.singletonList(ipo), 3600 /* t1 */, 4500 /* t2 */,
5130                 true /* shouldReplyRapidCommit */);
5131         verify(mCb, never()).onProvisioningSuccess(any());
5132     }
5133 
5134     private void runDhcp6PdNotStartInDualStackTest(final String prefix, final String dnsServer)
5135             throws Exception {
5136         final List<ByteBuffer> options = new ArrayList<>();
5137         if (prefix != null) {
5138             options.add(buildPioOption(3600, 1800, prefix));
5139         }
5140         if (dnsServer != null) {
5141             options.add(buildRdnssOption(3600, dnsServer));
5142         }
5143         options.add(buildSllaOption());
5144         final ByteBuffer ra = buildRaPacket(options.toArray(new ByteBuffer[options.size()]));
5145 
5146         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
5147                 .build();
5148         setDhcpFeatures(true /* isRapidCommitEnabled */,
5149                 false /* isDhcpIpConflictDetectEnabled */);
5150         startIpClientProvisioning(config);
5151 
5152         waitForRouterSolicitation();
5153         mPacketReader.sendResponse(ra);
5154 
5155         // Start IPv4 provisioning and wait until entire provisioning completes.
5156         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
5157                 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
5158         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any());
5159     }
5160 
5161     @Test
5162     public void testDhcp6Pd_notStartWithGlobalPio() throws Exception {
5163         runDhcp6PdNotStartInDualStackTest("2001:db8:1::/64" /* prefix */,
5164                 "2001:4860:4860::64" /* dnsServer */);
5165         // Reply with a normal RA with global prefix and an off-link DNS for IPv6 provisioning,
5166         // DHCPv6 prefix delegation should not start.
5167         assertNull(getNextDhcp6Packet(PACKET_TIMEOUT_MS));
5168     }
5169 
5170     @Test
5171     public void testDhcp6Pd_notStartWithUlaPioAndDns() throws Exception {
5172         runDhcp6PdNotStartInDualStackTest("fd7c:9df8:7f39:dc89::/64" /* prefix */,
5173                 "fd7c:9df8:7f39:dc89::1"  /* dnsServer */);
5174         // Reply with a normal RA even with ULA prefix and on-link ULA DNS for IPv6 provisioning,
5175         // DHCPv6 prefix delegation should not start.
5176         assertNull(getNextDhcp6Packet(PACKET_TIMEOUT_MS));
5177     }
5178 
5179     @Test
5180     public void testDhcp6Pd_notStartWithUlaPioAndOffLinkDns() throws Exception {
5181         runDhcp6PdNotStartInDualStackTest("fd7c:9df8:7f39:dc89::/64" /* prefix */,
5182                 "2001:4860:4860::64"  /* dnsServer */);
5183         // Reply with a normal RA even with ULA prefix and off-link DNS for IPv6 provisioning,
5184         // DHCPv6 prefix delegation should not start.
5185         assertNull(getNextDhcp6Packet(PACKET_TIMEOUT_MS));
5186     }
5187 
5188     @Test
5189     public void testDhcp6Pd_startWithNoNonIpv6LinkLocalAddresses() throws Exception {
5190         runDhcp6PdNotStartInDualStackTest(null /* prefix */,
5191                 "2001:4860:4860::64"  /* dnsServer */);
5192         // Reply with a normal RA with only RDNSS but no PIO for IPv6 provisioning,
5193         // DHCPv6 prefix delegation should start.
5194         final Dhcp6Packet packet = getNextDhcp6Packet(PACKET_TIMEOUT_MS);
5195         assertTrue(packet instanceof Dhcp6SolicitPacket);
5196     }
5197 
5198     @Test
5199     public void testDhcp6Pd_dualstack() throws Exception {
5200         final String dnsServer = "2001:4860:4860::64";
5201         final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer);
5202         final ByteBuffer ra = buildRaPacket(rdnss);
5203 
5204         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
5205                 .build();
5206         setDhcpFeatures(true /* isRapidCommitEnabled */,
5207                 false /* isDhcpIpConflictDetectEnabled */);
5208         startIpClientProvisioning(config);
5209 
5210         waitForRouterSolicitation();
5211         mPacketReader.sendResponse(ra);
5212 
5213         // Start IPv4 provisioning and wait until entire provisioning completes.
5214         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
5215                 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
5216         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any());
5217 
5218         // Start DHCPv6 Prefix Delegation.
5219         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5220         handleDhcp6Packets(prefix, false /* shouldReplyRapidCommit */);
5221         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(
5222                 x -> x.isIpv6Provisioned()
5223                         && hasIpv6AddressPrefixedWith(x, prefix)
5224                         && hasRouteTo(x, "2001:db8:1::/64", RTN_UNREACHABLE)
5225                         // IPv4 address, IPv6 link-local, two global delegated IPv6 addresses
5226                         && x.getLinkAddresses().size() == 4
5227         ));
5228     }
5229 
5230     @Test
5231     public void testDhcp6Pd_multiplePrefixesWithInvalidPrefix() throws Exception {
5232         final IpPrefix valid = new IpPrefix("2001:db8:1::/64");
5233         final IpPrefix invalid = new IpPrefix("2001:db8:2::/64"); // preferred lft > valid lft
5234         final IaPrefixOption validIpo = buildIaPrefixOption(valid, 4500 /* preferred */,
5235                 7200 /* valid */);
5236         final IaPrefixOption invalidIpo = buildIaPrefixOption(invalid, 4500 /* preferred */,
5237                 3000 /* valid */);
5238 
5239         prepareDhcp6PdTest();
5240         handleDhcp6Packets(Arrays.asList(invalidIpo, validIpo), 3600 /* t1 */, 4500 /* t2 */,
5241                 true /* shouldReplyRapidCommit */);
5242         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5243         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
5244         final LinkProperties lp = captor.getValue();
5245         assertTrue(hasIpv6AddressPrefixedWith(lp, valid));
5246         assertFalse(hasIpv6AddressPrefixedWith(lp, invalid));
5247     }
5248 
5249     @Test
5250     public void testDhcp6Pd_multiplePrefixesWithPrefixValidLifetimeOfZero() throws Exception {
5251         final IpPrefix valid = new IpPrefix("2001:db8:1::/64");
5252         final IpPrefix invalid = new IpPrefix("2001:db8:2::/64"); // preferred/valid lft 0
5253         final IaPrefixOption validIpo = buildIaPrefixOption(valid, 4500 /* preferred */,
5254                 7200 /* valid */);
5255         final IaPrefixOption invalidIpo = buildIaPrefixOption(invalid, 0 /* preferred */,
5256                 0 /* valid */);
5257 
5258         prepareDhcp6PdTest();
5259         handleDhcp6Packets(Arrays.asList(invalidIpo, validIpo), 3600 /* t1 */, 4500 /* t2 */,
5260                 true /* shouldReplyRapidCommit */);
5261         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5262         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
5263         final LinkProperties lp = captor.getValue();
5264         assertTrue(hasIpv6AddressPrefixedWith(lp, valid));
5265         assertFalse(hasIpv6AddressPrefixedWith(lp, invalid));
5266     }
5267 
5268     private void prepareDhcp6PdRenewTest() throws Exception {
5269         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5270         prepareDhcp6PdTest();
5271         handleDhcp6Packets(prefix, true /* shouldReplyRapidCommit */);
5272         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5273         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
5274         assertTrue(hasIpv6AddressPrefixedWith(captor.getValue(), prefix));
5275     }
5276 
5277     @Test
5278     @SignatureRequiredTest(reason = "Need to mock the DHCP6 renew/rebind alarms")
5279     public void testDhcp6Pd_renewAndRebind() throws Exception {
5280         prepareDhcp6PdRenewTest();
5281 
5282         final InOrder inOrder = inOrder(mAlarm);
5283         final Handler handler = mDependencies.mDhcp6Client.getHandler();
5284         final OnAlarmListener renewAlarm = expectAlarmSet(inOrder, "RENEW", 3600, handler);
5285         final OnAlarmListener rebindAlarm = expectAlarmSet(inOrder, "REBIND", 4500, handler);
5286 
5287         handler.post(() -> renewAlarm.onAlarm());
5288         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5289 
5290         Dhcp6Packet packet = getNextDhcp6Packet();
5291         assertTrue(packet instanceof Dhcp6RenewPacket);
5292 
5293         handler.post(() -> rebindAlarm.onAlarm());
5294         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5295 
5296         packet = getNextDhcp6Packet();
5297         assertTrue(packet instanceof Dhcp6RebindPacket);
5298     }
5299 
5300     @SignatureRequiredTest(reason = "Need to mock the DHCP6 renew/rebind alarms")
5301     @Test
5302     public void testDhcp6Pd_prefixMismatchOnRenew_newPrefix() throws Exception {
5303         prepareDhcp6PdRenewTest();
5304 
5305         final InOrder inOrder = inOrder(mAlarm);
5306         final Handler handler = mDependencies.mDhcp6Client.getHandler();
5307         final OnAlarmListener renewAlarm = expectAlarmSet(inOrder, "RENEW", 3600, handler);
5308 
5309         handler.post(() -> renewAlarm.onAlarm());
5310         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5311 
5312         Dhcp6Packet packet = getNextDhcp6Packet();
5313         assertTrue(packet instanceof Dhcp6RenewPacket);
5314 
5315         // Reply with a new prefix apart of the requested one, per RFC8415#section-18.2.10.1
5316         // any new prefix should be added.
5317         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5318         final IpPrefix prefix1 = new IpPrefix("2001:db8:2::/64");
5319         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 4500 /* preferred */,
5320                 7200 /* valid */);
5321         final IaPrefixOption ipo1 = buildIaPrefixOption(prefix1, 5000 /* preferred */,
5322                 6000 /* valid */);
5323         final PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 3600 /* t1 */,
5324                 4500 /* t2 */, Arrays.asList(ipo, ipo1));
5325         final ByteBuffer iapd = pd.build();
5326         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5327                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5328         verify(mCb, never()).onProvisioningFailure(any());
5329         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(
5330                 x -> x.isIpv6Provisioned()
5331                         && hasIpv6AddressPrefixedWith(x, prefix)
5332                         && hasIpv6AddressPrefixedWith(x, prefix1)
5333                         && hasRouteTo(x, "2001:db8:1::/64", RTN_UNREACHABLE)
5334                         && hasRouteTo(x, "2001:db8:2::/64", RTN_UNREACHABLE)
5335                         // IPv6 link-local, four global delegated IPv6 addresses
5336                         && x.getLinkAddresses().size() == 5
5337         ));
5338     }
5339 
5340     @SignatureRequiredTest(reason = "Need to mock the DHCP6 renew/rebind alarms")
5341     @Test
5342     public void testDhcp6Pd_prefixMismatchOnRenew_requestedPrefixAbsent() throws Exception {
5343         prepareDhcp6PdRenewTest();
5344 
5345         final InOrder inOrder = inOrder(mAlarm);
5346         final Handler handler = mDependencies.mDhcp6Client.getHandler();
5347         final OnAlarmListener renewAlarm = expectAlarmSet(inOrder, "RENEW", 3600, handler);
5348 
5349         handler.post(() -> renewAlarm.onAlarm());
5350         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5351 
5352         Dhcp6Packet packet = getNextDhcp6Packet();
5353         assertTrue(packet instanceof Dhcp6RenewPacket);
5354 
5355         // Reply with a new prefix but the requested one is absent, per RFC8415#section-18.2.10.1
5356         // the new prefix should be added and the absent prefix will expire in nature.
5357         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5358         final IpPrefix prefix1 = new IpPrefix("2001:db8:2::/64");
5359         final IaPrefixOption ipo = buildIaPrefixOption(prefix1, 4500 /* preferred */,
5360                 7200 /* valid */);
5361         final PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 3600 /* t1 */,
5362                 4500 /* t2 */, Arrays.asList(ipo));
5363         final ByteBuffer iapd = pd.build();
5364         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5365                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5366         verify(mCb, never()).onProvisioningFailure(any());
5367         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(
5368                 x -> x.isIpv6Provisioned()
5369                         && hasIpv6AddressPrefixedWith(x, prefix)
5370                         && hasIpv6AddressPrefixedWith(x, prefix1)
5371                         && hasRouteTo(x, "2001:db8:1::/64", RTN_UNREACHABLE)
5372                         && hasRouteTo(x, "2001:db8:2::/64", RTN_UNREACHABLE)
5373                         // IPv6 link-local, four global delegated IPv6 addresses
5374                         && x.getLinkAddresses().size() == 5
5375         ));
5376     }
5377 
5378     @SignatureRequiredTest(reason = "Need to mock the DHCP6 renew/rebind alarms")
5379     @Test
5380     public void testDhcp6Pd_prefixMismatchOnRenew_allPrefixesAbsent() throws Exception {
5381         prepareDhcp6PdRenewTest();
5382 
5383         final InOrder inOrder = inOrder(mAlarm);
5384         final Handler handler = mDependencies.mDhcp6Client.getHandler();
5385         final OnAlarmListener renewAlarm = expectAlarmSet(inOrder, "RENEW", 3600, handler);
5386 
5387         handler.post(() -> renewAlarm.onAlarm());
5388         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5389 
5390         Dhcp6Packet packet = getNextDhcp6Packet();
5391         assertTrue(packet instanceof Dhcp6RenewPacket);
5392 
5393         clearInvocations(mCb);
5394 
5395         // Reply with IA_PD but IA_Prefix is absent, client should still stay at the RenewState
5396         // and restransmit the Renew message, that should not result in any LinkProperties update.
5397         final PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 3600 /* t1 */,
5398                 4500 /* t2 */, new ArrayList<IaPrefixOption>(0));
5399         final ByteBuffer iapd = pd.build();
5400         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5401                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5402         verify(mCb, never()).onLinkPropertiesChange(any());
5403     }
5404 
5405     @SignatureRequiredTest(reason = "Need to mock the DHCP6 renew/rebind alarms")
5406     @Test
5407     public void testDhcp6Pd_renewInvalidPrefixes_zeroPreferredAndValidLifetime() throws Exception {
5408         prepareDhcp6PdRenewTest();
5409 
5410         final InOrder inOrder = inOrder(mAlarm);
5411         final Handler handler = mDependencies.mDhcp6Client.getHandler();
5412         final OnAlarmListener renewAlarm = expectAlarmSet(inOrder, "RENEW", 3600, handler);
5413 
5414         handler.post(() -> renewAlarm.onAlarm());
5415         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5416 
5417         Dhcp6Packet packet = getNextDhcp6Packet();
5418         assertTrue(packet instanceof Dhcp6RenewPacket);
5419 
5420         // Reply with the requested prefix with preferred/valid lifetime of 0.
5421         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5422         final IpPrefix prefix1 = new IpPrefix("2001:db8:2::/64");
5423         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 0 /* preferred */,
5424                 0 /* valid */);
5425         final IaPrefixOption ipo1 = buildIaPrefixOption(prefix1, 5000 /* preferred */,
5426                 6000 /* valid */);
5427         final PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 3600 /* t1 */,
5428                 4500 /* t2 */, Arrays.asList(ipo, ipo1));
5429         final ByteBuffer iapd = pd.build();
5430         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5431                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5432         verify(mCb, never()).onProvisioningFailure(any());
5433         // IPv6 addresses derived from prefix with 0 preferred/valid lifetime should be deleted.
5434         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(
5435                 x -> x.isIpv6Provisioned()
5436                         && !hasIpv6AddressPrefixedWith(x, prefix)
5437                         && hasIpv6AddressPrefixedWith(x, prefix1)
5438                         && !hasRouteTo(x, "2001:db8:1::/64", RTN_UNREACHABLE)
5439                         && hasRouteTo(x, "2001:db8:2::/64", RTN_UNREACHABLE)
5440                         // IPv6 link-local, two global delegated IPv6 addresses with prefix1
5441                         && x.getLinkAddresses().size() == 3
5442         ));
5443 
5444         handler.post(() -> renewAlarm.onAlarm());
5445         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5446 
5447         packet = getNextDhcp6Packet();
5448         assertTrue(packet instanceof Dhcp6RenewPacket);
5449         final List<IaPrefixOption> renewIpos = packet.getPrefixDelegation().ipos;
5450         assertEquals(1, renewIpos.size()); // don't renew prefix 2001:db8:1::/64 with 0
5451                                            // preferred/valid lifetime
5452         assertEquals(prefix1, renewIpos.get(0).getIpPrefix());
5453     }
5454 
5455     @SignatureRequiredTest(reason = "Need to mock the DHCP6 renew/rebind alarms")
5456     @Test
5457     public void testDhcp6Pd_renewInvalidPrefixes_theSameT1T2ValidLifetime() throws Exception {
5458         prepareDhcp6PdRenewTest();
5459 
5460         final InOrder inOrder = inOrder(mAlarm);
5461         final Handler handler = mDependencies.mDhcp6Client.getHandler();
5462         final OnAlarmListener renewAlarm = expectAlarmSet(inOrder, "RENEW", 3600, handler);
5463 
5464         handler.post(() -> renewAlarm.onAlarm());
5465         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5466 
5467         Dhcp6Packet packet = getNextDhcp6Packet();
5468         assertTrue(packet instanceof Dhcp6RenewPacket);
5469 
5470         clearInvocations(mCb);
5471 
5472         // Reply with the requested prefix with the same t1/t2/lifetime.
5473         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5474         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 3600 /* preferred */,
5475                 3600 /* valid */);
5476         final PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 3600 /* t1 */,
5477                 3600 /* t2 */, Collections.singletonList(ipo));
5478         final ByteBuffer iapd = pd.build();
5479         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5480                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5481         // The prefix doesn't change only the lifetime is updated, therefore, LinkProperties update
5482         // isn't expected.
5483         verify(mCb, never()).onProvisioningFailure(any());
5484         verify(mCb, never()).onLinkPropertiesChange(any());
5485 
5486         handler.post(() -> renewAlarm.onAlarm());
5487         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5488 
5489         packet = getNextDhcp6Packet(TEST_TIMEOUT_MS);
5490         assertNull(packet);
5491     }
5492 
5493     @Test
5494     public void testDhcp6Pd_multipleIaPrefixOptions() throws Exception {
5495         final InOrder inOrder = inOrder(mCb);
5496         final IpPrefix prefix1 = new IpPrefix("2001:db8:1::/64");
5497         final IpPrefix prefix2 = new IpPrefix("2400:db8:100::/64");
5498         final IpPrefix prefix3 = new IpPrefix("fd7c:9df8:7f39:dc89::/64");
5499         final IaPrefixOption ipo1 = buildIaPrefixOption(prefix1, 4500 /* preferred */,
5500                 7200 /* valid */);
5501         final IaPrefixOption ipo2 = buildIaPrefixOption(prefix2, 5600 /* preferred */,
5502                 6000 /* valid */);
5503         final IaPrefixOption ipo3 = buildIaPrefixOption(prefix3, 7200 /* preferred */,
5504                 14400 /* valid */);
5505         prepareDhcp6PdTest();
5506         handleDhcp6Packets(Arrays.asList(ipo1, ipo2, ipo3), 3600 /* t1 */, 4500 /* t2 */,
5507                 true /* shouldReplyRapidCommit */);
5508 
5509         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5510         verifyWithTimeout(inOrder, mCb).onProvisioningSuccess(captor.capture());
5511         LinkProperties lp = captor.getValue();
5512 
5513         // Sometimes privacy address or route may appear later along with onLinkPropertiesChange
5514         // callback, in this case we wait a bit longer to see all of these properties appeared and
5515         // then verify if they are what we are looking for.
5516         if (lp.getLinkAddresses().size() < 5) { // 1 IPv6 link-local and 4 global IPv6 addresses
5517                                                 // derived from prefix1 and prefix2
5518             final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
5519             verifyWithTimeout(inOrder, mCb).onLinkPropertiesChange(argThat(x -> {
5520                 if (!x.isIpv6Provisioned()) return false;
5521                 if (x.getLinkAddresses().size() != 5) return false;
5522                 lpFuture.complete(x);
5523                 return true;
5524             }));
5525             lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
5526         }
5527         assertNotNull(lp);
5528         assertTrue(hasIpv6AddressPrefixedWith(lp, prefix1));
5529         assertTrue(hasIpv6AddressPrefixedWith(lp, prefix2));
5530         assertFalse(hasIpv6AddressPrefixedWith(lp, prefix3));
5531         assertTrue(hasRouteTo(lp, prefix1.toString(), RTN_UNREACHABLE));
5532         assertTrue(hasRouteTo(lp, prefix2.toString(), RTN_UNREACHABLE));
5533         assertFalse(hasRouteTo(lp, prefix3.toString(), RTN_UNREACHABLE));
5534     }
5535 
5536     private void runDhcp6PacketWithNoPrefixAvailStatusCodeTest(boolean shouldReplyWithAdvertise)
5537             throws Exception {
5538         prepareDhcp6PdTest();
5539         Dhcp6Packet packet = getNextDhcp6Packet(PACKET_TIMEOUT_MS);
5540         assertTrue(packet instanceof Dhcp6SolicitPacket);
5541 
5542         final PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 0 /* t1 */, 0 /* t2 */,
5543                 new ArrayList<IaPrefixOption>() /* ipos */, Dhcp6Packet.STATUS_NO_PREFIX_AVAIL);
5544         final ByteBuffer iapd = pd.build();
5545         if (shouldReplyWithAdvertise) {
5546             mPacketReader.sendResponse(buildDhcp6Advertise(packet, iapd.array(), mClientMac,
5547                     (Inet6Address) mClientIpAddress));
5548         } else {
5549             mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5550                     (Inet6Address) mClientIpAddress, true /* rapidCommit */));
5551         }
5552 
5553         // Check if client will ignore Advertise or Reply for Rapid Commit Solicit and
5554         // retransmit Solicit.
5555         packet = getNextDhcp6Packet(PACKET_TIMEOUT_MS);
5556         assertTrue(packet instanceof Dhcp6SolicitPacket);
5557     }
5558 
5559     @Test
5560     public void testDhcp6AdvertiseWithNoPrefixAvailStatusCode() throws Exception {
5561         // Advertise
5562         runDhcp6PacketWithNoPrefixAvailStatusCodeTest(true /* shouldReplyWithAdvertise */);
5563     }
5564 
5565     @Test
5566     public void testDhcp6ReplyForRapidCommitSolicitWithNoPrefixAvailStatusCode() throws Exception {
5567         // Reply
5568         runDhcp6PacketWithNoPrefixAvailStatusCodeTest(false /* shouldReplyWithAdvertise */);
5569     }
5570 
5571     @Test
5572     public void testDhcp6ReplyForRequestWithNoPrefixAvailStatusCode() throws Exception {
5573         prepareDhcp6PdTest();
5574         Dhcp6Packet packet = getNextDhcp6Packet(PACKET_TIMEOUT_MS);
5575         assertTrue(packet instanceof Dhcp6SolicitPacket);
5576 
5577         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5578         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 4500 /* preferred */,
5579                 7200 /* valid */);
5580         PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 1000 /* t1 */,
5581                 2000 /* t2 */, Arrays.asList(ipo));
5582         ByteBuffer iapd = pd.build();
5583         mPacketReader.sendResponse(buildDhcp6Advertise(packet, iapd.array(), mClientMac,
5584                 (Inet6Address) mClientIpAddress));
5585 
5586         packet = getNextDhcp6Packet(PACKET_TIMEOUT_MS);
5587         assertTrue(packet instanceof Dhcp6RequestPacket);
5588 
5589         // Reply for Request with NoPrefixAvail status code. Not sure if this is reasonable in
5590         // practice, but Server can do everything it wants.
5591         pd = new PrefixDelegation(packet.getIaId(), 0 /* t1 */, 0 /* t2 */,
5592                 new ArrayList<IaPrefixOption>() /* ipos */, Dhcp6Packet.STATUS_NO_PREFIX_AVAIL);
5593         iapd = pd.build();
5594         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5595                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5596 
5597         // Check if client will ignore Reply for Request with NoPrefixAvail status code, and
5598         // rollback to SolicitState.
5599         packet = getNextDhcp6Packet(PACKET_TIMEOUT_MS);
5600         assertTrue(packet instanceof Dhcp6SolicitPacket);
5601     }
5602 
5603     @Test
5604     @SignatureRequiredTest(reason = "Need to mock the DHCP6 renew/rebind alarms")
5605     public void testDhcp6ReplyForRenewWithNoPrefixAvailStatusCode() throws Exception {
5606         prepareDhcp6PdRenewTest();
5607 
5608         final InOrder inOrder = inOrder(mAlarm);
5609         final Handler handler = mDependencies.mDhcp6Client.getHandler();
5610         final OnAlarmListener renewAlarm = expectAlarmSet(inOrder, "RENEW", 3600, handler);
5611 
5612         handler.post(() -> renewAlarm.onAlarm());
5613         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5614 
5615         Dhcp6Packet packet = getNextDhcp6Packet();
5616         assertTrue(packet instanceof Dhcp6RenewPacket);
5617 
5618         // Reply with normal IA_PD.
5619         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5620         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 4500 /* preferred */,
5621                 7200 /* valid */);
5622         PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 1000 /* t1 */,
5623                 2000 /* t2 */, Arrays.asList(ipo));
5624         ByteBuffer iapd = pd.build();
5625         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5626                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5627         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5628 
5629         // Trigger another Renew message.
5630         handler.post(() -> renewAlarm.onAlarm());
5631         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5632 
5633         packet = getNextDhcp6Packet();
5634         assertTrue(packet instanceof Dhcp6RenewPacket);
5635 
5636         // Reply for Renew with NoPrefixAvail status code, check if client will retransmit the
5637         // Renew message.
5638         pd = new PrefixDelegation(packet.getIaId(), 3600 /* t1 */, 4500 /* t2 */,
5639                 new ArrayList<IaPrefixOption>(0) /* ipos */, Dhcp6Packet.STATUS_NO_PREFIX_AVAIL);
5640         iapd = pd.build();
5641         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5642                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5643 
5644         packet = getNextDhcp6Packet(TEST_WAIT_RENEW_REBIND_RETRANSMIT_MS);
5645         assertTrue(packet instanceof Dhcp6RenewPacket);
5646     }
5647 
5648     @Test
5649     @SignatureRequiredTest(reason = "Need to mock the DHCP6 renew/rebind alarms")
5650     public void testDhcp6ReplyForRebindWithNoPrefixAvailStatusCode() throws Exception {
5651         prepareDhcp6PdRenewTest();
5652 
5653         final InOrder inOrder = inOrder(mAlarm);
5654         final Handler handler = mDependencies.mDhcp6Client.getHandler();
5655         final OnAlarmListener renewAlarm = expectAlarmSet(inOrder, "RENEW", 3600, handler);
5656         final OnAlarmListener rebindAlarm = expectAlarmSet(inOrder, "REBIND", 4500, handler);
5657 
5658         handler.post(() -> renewAlarm.onAlarm());
5659         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5660 
5661         Dhcp6Packet packet = getNextDhcp6Packet();
5662         assertTrue(packet instanceof Dhcp6RenewPacket);
5663 
5664         handler.post(() -> rebindAlarm.onAlarm());
5665         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5666 
5667         packet = getNextDhcp6Packet();
5668         assertTrue(packet instanceof Dhcp6RebindPacket);
5669 
5670         // Reply with normal IA_PD.
5671         final IpPrefix prefix = new IpPrefix("2001:db8:1::/64");
5672         final IaPrefixOption ipo = buildIaPrefixOption(prefix, 4500 /* preferred */,
5673                 7200 /* valid */);
5674         PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 1000 /* t1 */,
5675                 2000 /* t2 */, Arrays.asList(ipo));
5676         ByteBuffer iapd = pd.build();
5677         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5678                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5679         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5680 
5681         // Trigger another Rebind message.
5682         handler.post(() -> renewAlarm.onAlarm());
5683         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5684 
5685         packet = getNextDhcp6Packet();
5686         assertTrue(packet instanceof Dhcp6RenewPacket);
5687 
5688         handler.post(() -> rebindAlarm.onAlarm());
5689         HandlerUtils.waitForIdle(handler, TEST_TIMEOUT_MS);
5690 
5691         packet = getNextDhcp6Packet();
5692         assertTrue(packet instanceof Dhcp6RebindPacket);
5693 
5694         // Reply for Rebind with NoPrefixAvail status code, check if client will retransmit the
5695         // Rebind message.
5696         pd = new PrefixDelegation(packet.getIaId(), 3600 /* t1 */,
5697                 4500 /* t2 */, new ArrayList<IaPrefixOption>(0) /* ipos */,
5698                 Dhcp6Packet.STATUS_NO_PREFIX_AVAIL);
5699         iapd = pd.build();
5700         mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac,
5701                 (Inet6Address) mClientIpAddress, false /* rapidCommit */));
5702 
5703         packet = getNextDhcp6Packet(TEST_WAIT_RENEW_REBIND_RETRANSMIT_MS);
5704         assertTrue(packet instanceof Dhcp6RebindPacket);
5705     }
5706 
5707     @Test
5708     @SignatureRequiredTest(reason = "InterfaceParams.getByName requires CAP_NET_ADMIN")
5709     public void testSendRtmDelAddressMethod() throws Exception {
5710         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
5711                 .withoutIPv4()
5712                 .build();
5713         startIpClientProvisioning(config);
5714 
5715         final LinkProperties lp = doIpv6OnlyProvisioning();
5716         assertNotNull(lp);
5717         assertEquals(3, lp.getLinkAddresses().size()); // IPv6 privacy, stable privacy, link-local
5718 
5719         clearInvocations(mCb);
5720 
5721         // Delete all global IPv6 addresses, then that will trigger onProvisioningFailure callback.
5722         final InterfaceParams params = InterfaceParams.getByName(mIfaceName);
5723         for (LinkAddress la : lp.getLinkAddresses()) {
5724             if (la.isGlobalPreferred()) {
5725                 NetlinkUtils.sendRtmDelAddressRequest(params.index, (Inet6Address) la.getAddress(),
5726                         (short) la.getPrefixLength());
5727                 verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(
5728                         x -> !x.getLinkAddresses().contains(la)
5729                 ));
5730             }
5731         }
5732         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
5733     }
5734 
5735     @Test
5736     @SignatureRequiredTest(reason = "requires mocked netd to read/write IPv6 sysctl")
5737     public void testIpv6SysctlsRestAfterStoppingIpClient() throws Exception {
5738         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
5739                 .withoutIPv4()
5740                 .build();
5741         // dad_transmits has been set to 0 in disableIpv6ProvisioningDelays, re-enable
5742         // dad_transmits for testing, production code will restore all IPv6 sysctls at
5743         // StoppedState#enter anyway, read this parameter value after IpClient shutdown
5744         // to check if that's default value 1.
5745         mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits", "1");
5746         startIpClientProvisioning(config);
5747         verify(mNetd, timeout(TEST_TIMEOUT_MS)).interfaceSetEnableIPv6(mIfaceName, true);
5748         doIpv6OnlyProvisioning();
5749 
5750         // Shutdown IpClient and check if the IPv6 sysctls: accept_ra, accept_ra_defrtr and
5751         // dad_transmits have been reset to the default values.
5752         mIpc.shutdown();
5753         awaitIpClientShutdown();
5754         final int dadTransmits = Integer.parseUnsignedInt(
5755                 mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits"));
5756         assertEquals(1, dadTransmits);
5757         final int acceptRa = Integer.parseUnsignedInt(
5758                 mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "accept_ra"));
5759         assertEquals(2, acceptRa);
5760         final int acceptRaDefRtr = Integer.parseUnsignedInt(
5761                 mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "accept_ra_defrtr"));
5762         assertEquals(1, acceptRaDefRtr);
5763     }
5764 
5765     private void runDhcpDomainSearchListOptionTest(final String domainName,
5766             final List<String> domainSearchList, final String expectedDomain) throws Exception {
5767         when(mResources.getBoolean(R.bool.config_dhcp_client_domain_search_list)).thenReturn(true);
5768         final ProvisioningConfiguration cfg = new ProvisioningConfiguration.Builder()
5769                 .withoutIpReachabilityMonitor()
5770                 .withoutIPv6()
5771                 .withCreatorUid(TEST_DEVICE_OWNER_APP_UID)
5772                 .build();
5773 
5774         startIpClientProvisioning(cfg);
5775         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
5776                 false /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU,
5777                 null /* captivePortalApiUrl */, null /* ipv6OnlyWaitTime */,
5778                 domainName, domainSearchList);
5779 
5780         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5781         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
5782         final LinkProperties lp = captor.getValue();
5783         assertNotNull(lp);
5784         assertEquals(expectedDomain, lp.getDomains());
5785     }
5786 
5787     @Test
5788     @SignatureRequiredTest(reason = "requires mocked DevicePolicyManager")
5789     public void testDhcpDomainSearchListOption() throws Exception {
5790         final String domainName = "google.com";
5791         final List<String> searchList = List.of("suffix1.google.com", "suffix2.google.com");
5792         final String expectedDomain = "google.com suffix1.google.com suffix2.google.com";
5793         runDhcpDomainSearchListOptionTest(domainName, searchList, expectedDomain);
5794     }
5795 
5796     @Test
5797     @SignatureRequiredTest(reason = "requires mocked DevicePolicyManager")
5798     public void testDhcpDomainSearchListOption_invalidSuffix() throws Exception {
5799         final String domainName = "google.com";
5800         final List<String> searchList = List.of("google com");
5801         runDhcpDomainSearchListOptionTest(domainName, searchList, domainName /* expectedDomain */);
5802     }
5803 
5804     @Test
5805     @SignatureRequiredTest(reason = "requires mocked DevicePolicyManager")
5806     public void testDhcpDomainSearchListOption_onlySearchList() throws Exception {
5807         final List<String> searchList = List.of("google.com", "example.com");
5808         final String expectedDomain = "google.com example.com";
5809         runDhcpDomainSearchListOptionTest(null /* domainName */, searchList,
5810                 expectedDomain);
5811     }
5812 
5813     private void assertLinkAddressDeprecationTime(final LinkAddress la, final long when) {
5814         assertTrue(la.getDeprecationTime() != LinkAddress.LIFETIME_UNKNOWN);
5815         // Allow +/- 2 seconds to prevent flaky tests
5816         assertTrue(la.getDeprecationTime() < when + TEST_LIFETIME_TOLERANCE_MS);
5817         assertTrue(la.getDeprecationTime() > when - TEST_LIFETIME_TOLERANCE_MS);
5818     }
5819 
5820     private void assertLinkAddressExpirationTime(final LinkAddress la, final long when) {
5821         assertTrue(la.getExpirationTime() != LinkAddress.LIFETIME_UNKNOWN);
5822         // Allow +/- 2 seconds to prevent flaky tests
5823         assertTrue(la.getExpirationTime() < when + TEST_LIFETIME_TOLERANCE_MS);
5824         assertTrue(la.getExpirationTime() > when - TEST_LIFETIME_TOLERANCE_MS);
5825     }
5826 
5827     private void assertLinkAddressPermanentLifetime(final LinkAddress la) {
5828         assertEquals(LinkAddress.LIFETIME_PERMANENT, la.getDeprecationTime());
5829         assertEquals(LinkAddress.LIFETIME_PERMANENT, la.getExpirationTime());
5830     }
5831 
5832     @Test
5833     @Flag(name = IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION, enabled = true)
5834     public void testPopulateLinkAddressLifetime() throws Exception {
5835         final LinkProperties lp = doDualStackProvisioning();
5836         final long now = SystemClock.elapsedRealtime();
5837         long when = 0;
5838         for (LinkAddress la : lp.getLinkAddresses()) {
5839             if (la.isIpv4()) {
5840                 when = now + 3600 * 1000; // DHCP lease duration
5841                 assertLinkAddressDeprecationTime(la, when);
5842                 assertLinkAddressExpirationTime(la, when);
5843             } else if (la.isIpv6() && la.getAddress().isLinkLocalAddress()) {
5844                 assertLinkAddressPermanentLifetime(la);
5845             } else if (la.isIpv6() && la.isGlobalPreferred()) {
5846                 when = now + 1800 * 1000; // preferred=1800s
5847                 assertLinkAddressDeprecationTime(la, when);
5848                 when = now + 3600 * 1000; // valid=3600s
5849                 assertLinkAddressExpirationTime(la, when);
5850             }
5851         }
5852     }
5853 
5854     @Test
5855     @Flag(name = IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION, enabled = true)
5856     public void testPopulateLinkAddressLifetime_infiniteLeaseDuration() throws Exception {
5857         final ProvisioningConfiguration cfg = new ProvisioningConfiguration.Builder()
5858                 .withoutIPv6()
5859                 .build();
5860 
5861         startIpClientProvisioning(cfg);
5862         handleDhcpPackets(true /* isSuccessLease */, DhcpPacket.INFINITE_LEASE,
5863                 false /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU,
5864                 null /* captivePortalApiUrl */, null /* ipv6OnlyWaitTime */,
5865                 null /* domainName */, null /* domainSearchList */);
5866 
5867         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5868         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
5869         final LinkProperties lp = captor.getValue();
5870         assertNotNull(lp);
5871         for (LinkAddress la : lp.getLinkAddresses()) {
5872             if (la.isIpv4()) {
5873                 assertLinkAddressPermanentLifetime(la);
5874             }
5875         }
5876     }
5877 
5878     @Test
5879     @Flag(name = IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION, enabled = true)
5880     public void testPopulateLinkAddressLifetime_minimalLeaseDuration() throws Exception {
5881         final ProvisioningConfiguration cfg = new ProvisioningConfiguration.Builder()
5882                 .withoutIPv6()
5883                 .build();
5884 
5885         startIpClientProvisioning(cfg);
5886         handleDhcpPackets(true /* isSuccessLease */, 59 /* lease duration */,
5887                 false /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU,
5888                 null /* captivePortalApiUrl */, null /* ipv6OnlyWaitTime */,
5889                 null /* domainName */, null /* domainSearchList */);
5890 
5891         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
5892         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
5893         final LinkProperties lp = captor.getValue();
5894         assertNotNull(lp);
5895         for (LinkAddress la : lp.getLinkAddresses()) {
5896             if (la.isIpv4()) {
5897                 final long now = SystemClock.elapsedRealtime();
5898                 final long when = now + 60 * 1000; // minimal lease duration
5899                 assertLinkAddressDeprecationTime(la, when);
5900                 assertLinkAddressExpirationTime(la, when);
5901             }
5902         }
5903     }
5904 
5905     @Test
5906     @Flag(name = IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION, enabled = true)
5907     public void testPopulateLinkAddressLifetime_onDhcpRenew() throws Exception {
5908         final ProvisioningConfiguration cfg = new ProvisioningConfiguration.Builder()
5909                 .withoutIPv6()
5910                 .build();
5911         setDeviceConfigProperty(CONFIG_MINIMUM_LEASE,  5/* default minimum lease */);
5912         startIpClientProvisioning(cfg);
5913         handleDhcpPackets(true /* isSuccessLease */, 4 /* lease duration */,
5914                 false /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU,
5915                 null /* captivePortalApiUrl */, null /* ipv6OnlyWaitTime */,
5916                 null /* domainName */, null /* domainSearchList */);
5917 
5918         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any());
5919 
5920         // Device sends ARP request for address resolution of default gateway first.
5921         final ArpPacket request = getNextArpPacket();
5922         assertArpRequest(request, SERVER_ADDR);
5923         sendArpReply(request.senderHwAddress.toByteArray() /* dst */, ROUTER_MAC_BYTES /* srcMac */,
5924                 request.senderIp /* target IP */, SERVER_ADDR /* sender IP */);
5925 
5926         // Then client sends unicast DHCPREQUEST to extend the IPv4 address lifetime, and we reply
5927         // with DHCPACK to refresh the DHCP lease.
5928         final DhcpPacket packet = getNextDhcpPacket();
5929         assertTrue(packet instanceof DhcpRequestPacket);
5930         assertDhcpRequestForReacquire(packet);
5931         mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR,
5932                 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU,
5933                 false /* rapidCommit */, null /* captivePortalApiUrl */));
5934 
5935         // Once the IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION flag is enabled, the IP
5936         // lease will be refreshed as well as the link address lifetime by transiting to
5937         // ConfiguringInterfaceState, where IpClient sends a new RTM_NEWADDR message to kernel
5938         // to update the IPv4 address, therefore, we should never see provisioning failure any
5939         // more.
5940         verify(mCb, never()).onProvisioningFailure(any());
5941     }
5942 
5943     private void doDhcpHostnameSettingTest(int hostnameSetting,
5944             boolean isHostnameConfigurationEnabled, boolean expectSendHostname) throws Exception {
5945         final ProvisioningConfiguration cfg = new ProvisioningConfiguration.Builder()
5946                 .withoutIPv6()
5947                 .withHostnameSetting(hostnameSetting)
5948                 .build();
5949         final String expectedHostname;
5950         final String expectedHostnameAfterTransliteration;
5951         if (mDependencies != null) {
5952             mDependencies.setHostnameConfiguration(isHostnameConfigurationEnabled,
5953                     TEST_HOST_NAME);
5954             expectedHostname = TEST_HOST_NAME;
5955             expectedHostnameAfterTransliteration = TEST_HOST_NAME_TRANSLITERATION;
5956         } else {
5957             expectedHostname = Settings.Global.getString(
5958                     InstrumentationRegistry.getInstrumentation().getContext().getContentResolver(),
5959                     Settings.Global.DEVICE_NAME);
5960             expectedHostnameAfterTransliteration = new HostnameTransliterator()
5961                     .transliterate(expectedHostname);
5962         }
5963         startIpClientProvisioning(cfg);
5964 
5965         // perform DHCP handshake and capture the packets sent from client such as
5966         // DHCPDISCOVER and DHCPREQUEST.
5967         final List<DhcpPacket> sentPackets = handleDhcpPackets(true /* isSuccessLease */,
5968                 DhcpPacket.INFINITE_LEASE,
5969                 false /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU,
5970                 null /* captivePortalApiUrl */, null /* ipv6OnlyWaitTime */,
5971                 null /* domainName */, null /* domainSearchList */);
5972 
5973         // check if the DHCP packet sent from the client takes a hostname option per different
5974         // configs. Do not consider the null hostname case.
5975         assertHostname(expectSendHostname, expectedHostname, expectedHostnameAfterTransliteration,
5976                 sentPackets);
5977     }
5978 
5979     @Test
5980     @SignatureRequiredTest(reason = "need to mock setHostnameConfiguration")
5981     public void testHostname_hostnameSettingUnset_enableHostnameConfig() throws Exception {
5982         // If hostname setting is unset but legacy hostname overlay config is enabled,
5983         // we expect that the DHCP packet takes a hostname option.
5984         doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_UNSET,
5985                 true /* isHostnameConfigurationEnabled */, true /* expectSendHostname */);
5986     }
5987 
5988     @Test
5989     @SignatureRequiredTest(reason = "need to mock setHostnameConfiguration")
5990     public void testHostname_hostnameSettingUnset_disableHostnameConfig() throws Exception {
5991         // If hostname setting is unset and legacy hostname overlay config is disabled,
5992         // we expect that the DHCP packet doesn't take a hostname option.
5993         doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_UNSET,
5994                 false /* isHostnameConfigurationEnabled */, false /* expectSendHostname */);
5995     }
5996 
5997     @Test
5998     public void testHostname_hostnameSettingSend_enableHostnameConfig() throws Exception {
5999         // If hostname setting is set and legacy hostname overlay config is enabled,
6000         // we expect that the DHCP packet takes a hostname option.
6001         doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_SEND,
6002                 true /* isHostnameConfigurationEnabled */, true /* expectSendHostname */);
6003     }
6004 
6005     @Test
6006     public void testHostname_hostnameSettingSend_disableHostnameConfig() throws Exception {
6007         // If hostname setting is set and legacy hostname overlay config is disabled,
6008         // we still expect that the DHCP packet takes a hostname option.
6009         doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_SEND,
6010                 false /* isHostnameConfigurationEnabled */, true /* expectSendHostname */);
6011     }
6012 
6013     @Test
6014     public void testHostname_hostnameSettingNotSend_enableHostnameConfig() throws Exception {
6015         // If hostname setting is not send and even if legacy hostname overlay config is
6016         // enabled, we expect that the DHCP packet doesn't take a hostname option.
6017         doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_DO_NOT_SEND,
6018                 true /* isHostnameConfigurationEnabled */, false /* expectSendHostname */);
6019     }
6020 
6021     @Test
6022     public void testHostname_hostnameSettingNotSend_disableHostnameConfig() throws Exception {
6023         // If hostname setting is not send and even if legacy hostname overlay config is
6024         // disabled, we expect that the DHCP packet doesn't take a hostname option.
6025         doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_DO_NOT_SEND,
6026                 false /* isHostnameConfigurationEnabled */, false /* expectSendHostname */);
6027     }
6028 
6029     // Store the network event to database multiple times.
6030     private void storeNudFailureEvents(long when, long expiry, int times, int eventType) {
6031         for (int i = 0; i < times; i++) {
6032             storeNetworkEvent(TEST_CLUSTER, when, expiry, eventType);
6033             when += 60 * 1000; // event interval is 1min
6034             expiry += 60 * 1000; // expiry also delays 1min
6035         }
6036     }
6037 
6038     @Test
6039     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = true)
6040     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6041     public void testIgnoreNudFailuresIfTooManyInPastDay() throws Exception {
6042         // // NUD failure event count exceeds daily threshold nor weekly.
6043         final long when = System.currentTimeMillis() - ONE_DAY_IN_MS / 2; // 12h ago
6044         final long expiry = when + ONE_WEEK_IN_MS;
6045         storeNudFailureEvents(when, expiry, 10, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM);
6046 
6047         runIpReachabilityMonitorProbeFailedTest();
6048         assertNeverNotifyNeighborLost();
6049     }
6050 
6051     @Test
6052     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
6053     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = false)
6054     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6055     public void testIgnoreNudFailuresIfTooManyInPastDay_flagOff() throws Exception {
6056         // NUD failure event count exceeds daily threshold nor weekly.
6057         final long when = System.currentTimeMillis() - ONE_DAY_IN_MS / 2; // 12h ago
6058         final long expiry = when + ONE_WEEK_IN_MS;
6059         storeNudFailureEvents(when, expiry, 19, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM);
6060 
6061         runIpReachabilityMonitorProbeFailedTest();
6062         assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
6063                 NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL);
6064     }
6065 
6066     @Test
6067     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
6068     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = true)
6069     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6070     public void testIgnoreNudFailuresIfTooManyInPastDay_notUpToThreshold()
6071             throws Exception {
6072         // NUD failure event count doesn't exceed either weekly threshold nor daily.
6073         final long when = System.currentTimeMillis() - ONE_DAY_IN_MS / 2; // 12h ago
6074         final long expiry = when + ONE_WEEK_IN_MS;
6075         storeNudFailureEvents(when, expiry, 9, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM);
6076 
6077         runIpReachabilityMonitorProbeFailedTest();
6078         assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
6079                 NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL);
6080     }
6081 
6082     @Test
6083     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = true)
6084     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6085     public void testIgnoreNudFailuresIfTooManyInPastWeek() throws Exception {
6086         // NUD failure event count exceeds the weekly threshold, but not daily threshold in the past
6087         // day.
6088         long when = System.currentTimeMillis() - ONE_WEEK_IN_MS / 2; // half a week ago
6089         long expiry = when + ONE_WEEK_IN_MS;
6090         storeNudFailureEvents(when, expiry, 11, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM);
6091 
6092         when = System.currentTimeMillis() - ONE_DAY_IN_MS / 2; // 12h ago
6093         expiry = when + ONE_WEEK_IN_MS;
6094         storeNudFailureEvents(when, expiry, 9, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM);
6095 
6096         runIpReachabilityMonitorProbeFailedTest();
6097         assertNeverNotifyNeighborLost();
6098     }
6099 
6100     @Test
6101     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
6102     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = false)
6103     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6104     public void testIgnoreNudFailuresIfTooManyInPastWeek_flagOff() throws Exception {
6105         // NUD failure event count exceeds the weekly threshold, but not daily threshold in the past
6106         // day.
6107         long when = System.currentTimeMillis() - ONE_WEEK_IN_MS / 2; // half a week ago
6108         long expiry = when + ONE_WEEK_IN_MS;
6109         storeNudFailureEvents(when, expiry, 11, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM);
6110 
6111         when = System.currentTimeMillis() - ONE_DAY_IN_MS / 2; // 12h ago
6112         expiry = when + ONE_WEEK_IN_MS;
6113         storeNudFailureEvents(when, expiry, 9, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM);
6114 
6115         runIpReachabilityMonitorProbeFailedTest();
6116         assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
6117                 NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL);
6118     }
6119 
6120     @Test
6121     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
6122     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = true)
6123     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6124     public void testIgnoreNudFailuresIfTooManyInPastWeek_notUpToThreshold() throws Exception {
6125         // NUD failure event count doesn't exceed either weekly threshold nor daily.
6126         long when = System.currentTimeMillis() - ONE_WEEK_IN_MS / 2; // half a week ago
6127         long expiry = when + ONE_WEEK_IN_MS;
6128         storeNudFailureEvents(when, expiry, 10, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM);
6129 
6130         when = System.currentTimeMillis() - ONE_DAY_IN_MS / 2; // 12h ago
6131         expiry = when + ONE_WEEK_IN_MS;
6132         storeNudFailureEvents(when, expiry, 9, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM);
6133 
6134         runIpReachabilityMonitorProbeFailedTest();
6135         assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
6136                 NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL);
6137     }
6138 
6139     @Test
6140     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = true)
6141     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6142     public void testIgnoreNudFailuresIfTooManyInPastWeek_stopWritingEvent() throws Exception {
6143         long when = (long) (System.currentTimeMillis() - SIX_HOURS_IN_MS * 0.9);
6144         long expiry = when + ONE_WEEK_IN_MS;
6145         storeNudFailureEvents(when, expiry, 10, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC);
6146 
6147         runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
6148                 ROUTER_LINK_LOCAL /* targetIp */,
6149                 false /* expectNeighborLost */);
6150         verify(mIpMemoryStore, never()).storeNetworkEvent(any(), anyLong(), anyLong(),
6151                 eq(IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC), any());
6152     }
6153 
6154     @Test
6155     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, enabled = false)
6156     @Flag(name = IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, enabled = false)
6157     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
6158     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = true)
6159     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6160     public void testIgnoreNudFailuresStopWritingEvents() throws Exception {
6161         // Add enough failures that NUD failures are ignored.
6162         long when = (long) (System.currentTimeMillis() - SIX_HOURS_IN_MS * 1.1);
6163         long expiry = when + ONE_WEEK_IN_MS;
6164         storeNudFailureEvents(when, expiry, 10, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC);
6165 
6166         // Add enough recent failures to almost, but not quite reach the 6-hour threshold.
6167         when = (long) (System.currentTimeMillis() - SIX_HOURS_IN_MS * 0.1);
6168         expiry = when + ONE_WEEK_IN_MS;
6169         storeNudFailureEvents(when, expiry, 9, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC);
6170 
6171         prepareIpReachabilityMonitorAddressResolutionTest(IPV6_ON_LINK_DNS_SERVER,
6172                 ROUTER_LINK_LOCAL);
6173 
6174         // The first new failure is ignored and written to the database.
6175         // The total is 10 failures in the last 6 hours.
6176         sendPacketToUnreachableNeighbor(ipv6Addr(IPV6_OFF_LINK_DNS_SERVER));
6177         expectAndDropMulticastNses(ROUTER_LINK_LOCAL, false /* expectNeighborLost */);
6178         verify(mIpMemoryStore).storeNetworkEvent(any(), anyLong(), anyLong(),
6179                 eq(IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC), any());
6180 
6181         // The second new failure is ignored, but not written.
6182         reset(mIpMemoryStore);
6183         sendPacketToUnreachableNeighbor(ipv6Addr(IPV6_ON_LINK_DNS_SERVER));
6184         expectAndDropMulticastNses(ipv6Addr(IPV6_ON_LINK_DNS_SERVER),
6185                 false /* expectNeighborLost */);
6186         verifyNoMoreInteractions(mIpMemoryStore);
6187     }
6188 
6189     @Test
6190     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
6191     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = false)
6192     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6193     public void testIgnoreNudFailuresIfTooManyInPastWeek_stopWritingEvent_flagOff()
6194             throws Exception {
6195         long when = (long) (System.currentTimeMillis() - SIX_HOURS_IN_MS * 0.9);
6196         long expiry = when + ONE_WEEK_IN_MS;
6197         storeNudFailureEvents(when, expiry, 10, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC);
6198 
6199         runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
6200                 ROUTER_LINK_LOCAL /* targetIp */,
6201                 true /* expectNeighborLost */);
6202         verify(mIpMemoryStore, never()).storeNetworkEvent(any(), anyLong(), anyLong(),
6203                 eq(IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC), any());
6204     }
6205 
6206     @Test
6207     @Flag(name = IP_REACHABILITY_IGNORE_NEVER_REACHABLE_NEIGHBOR_VERSION, enabled = false)
6208     @Flag(name = IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION, enabled = true)
6209     @SignatureRequiredTest(reason = "need to delete cluster from real db in tearDown")
6210     public void testIgnoreNudFailuresIfTooManyInPastWeek_stopWritingEvent_notUpToThreshold()
6211             throws Exception {
6212         long when = (long) (System.currentTimeMillis() - SIX_HOURS_IN_MS * 0.9);
6213         long expiry = when + ONE_WEEK_IN_MS;
6214         storeNudFailureEvents(when, expiry, 9, IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC);
6215 
6216         runIpReachabilityMonitorAddressResolutionTest(IPV6_OFF_LINK_DNS_SERVER,
6217                 ROUTER_LINK_LOCAL /* targetIp */,
6218                 true /* expectNeighborLost */);
6219         assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */,
6220                 NudEventType.NUD_ORGANIC_FAILED_CRITICAL);
6221         verify(mIpMemoryStore).storeNetworkEvent(any(), anyLong(), anyLong(),
6222                 eq(IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC), any());
6223     }
6224 }
6225