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