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.net.dhcp.DhcpClient.EXPIRED_LEASE;
20 import static android.net.dhcp.DhcpPacket.DHCP_BOOTREQUEST;
21 import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
22 import static android.net.dhcp.DhcpPacket.DHCP_IPV6_ONLY_PREFERRED;
23 import static android.net.dhcp.DhcpPacket.DHCP_MAGIC_COOKIE;
24 import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
25 import static android.net.dhcp.DhcpPacket.ENCAP_L2;
26 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
27 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
28 import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS;
29 import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable;
30 import static android.net.ipmemorystore.Status.SUCCESS;
31 import static android.system.OsConstants.ETH_P_IPV6;
32 import static android.system.OsConstants.IFA_F_TEMPORARY;
33 import static android.system.OsConstants.IPPROTO_ICMPV6;
34 
35 import static com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress;
36 import static com.android.net.module.util.Inet4AddressUtils.getPrefixMaskAsInet4Address;
37 import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY;
38 import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST;
39 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
40 import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST;
41 import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN;
42 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_OFFSET;
43 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
44 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
45 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
46 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
47 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST;
48 import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
49 import static com.android.net.module.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
50 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
51 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
52 
53 import static junit.framework.Assert.fail;
54 
55 import static org.junit.Assert.assertArrayEquals;
56 import static org.junit.Assert.assertEquals;
57 import static org.junit.Assert.assertFalse;
58 import static org.junit.Assert.assertNotEquals;
59 import static org.junit.Assert.assertNotNull;
60 import static org.junit.Assert.assertNull;
61 import static org.junit.Assert.assertTrue;
62 import static org.junit.Assume.assumeFalse;
63 import static org.junit.Assume.assumeTrue;
64 import static org.mockito.ArgumentMatchers.anyInt;
65 import static org.mockito.ArgumentMatchers.contains;
66 import static org.mockito.ArgumentMatchers.longThat;
67 import static org.mockito.Mockito.any;
68 import static org.mockito.Mockito.argThat;
69 import static org.mockito.Mockito.atLeastOnce;
70 import static org.mockito.Mockito.doAnswer;
71 import static org.mockito.Mockito.doThrow;
72 import static org.mockito.Mockito.eq;
73 import static org.mockito.Mockito.inOrder;
74 import static org.mockito.Mockito.mock;
75 import static org.mockito.Mockito.never;
76 import static org.mockito.Mockito.reset;
77 import static org.mockito.Mockito.spy;
78 import static org.mockito.Mockito.timeout;
79 import static org.mockito.Mockito.times;
80 import static org.mockito.Mockito.verify;
81 import static org.mockito.Mockito.when;
82 
83 import android.app.AlarmManager;
84 import android.app.AlarmManager.OnAlarmListener;
85 import android.app.Instrumentation;
86 import android.content.ContentResolver;
87 import android.content.Context;
88 import android.content.res.Resources;
89 import android.net.ConnectivityManager;
90 import android.net.DhcpResults;
91 import android.net.DhcpResultsParcelable;
92 import android.net.INetd;
93 import android.net.InetAddresses;
94 import android.net.InterfaceConfigurationParcel;
95 import android.net.IpPrefix;
96 import android.net.Layer2InformationParcelable;
97 import android.net.Layer2PacketParcelable;
98 import android.net.LinkAddress;
99 import android.net.LinkProperties;
100 import android.net.MacAddress;
101 import android.net.NetworkStackIpMemoryStore;
102 import android.net.TestNetworkInterface;
103 import android.net.TestNetworkManager;
104 import android.net.Uri;
105 import android.net.dhcp.DhcpClient;
106 import android.net.dhcp.DhcpDeclinePacket;
107 import android.net.dhcp.DhcpDiscoverPacket;
108 import android.net.dhcp.DhcpPacket;
109 import android.net.dhcp.DhcpPacket.ParseException;
110 import android.net.dhcp.DhcpRequestPacket;
111 import android.net.ipmemorystore.NetworkAttributes;
112 import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
113 import android.net.ipmemorystore.Status;
114 import android.net.netlink.StructNdOptPref64;
115 import android.net.networkstack.TestNetworkStackServiceClient;
116 import android.net.networkstack.aidl.dhcp.DhcpOption;
117 import android.net.shared.Layer2Information;
118 import android.net.shared.ProvisioningConfiguration;
119 import android.net.shared.ProvisioningConfiguration.ScanResultInfo;
120 import android.net.util.InterfaceParams;
121 import android.net.util.NetworkStackUtils;
122 import android.os.Build;
123 import android.os.Handler;
124 import android.os.HandlerThread;
125 import android.os.IBinder;
126 import android.os.ParcelFileDescriptor;
127 import android.os.PowerManager;
128 import android.os.RemoteException;
129 import android.os.SystemClock;
130 import android.os.SystemProperties;
131 import android.stats.connectivity.NetworkQuirkEvent;
132 import android.system.ErrnoException;
133 import android.system.Os;
134 
135 import androidx.annotation.NonNull;
136 import androidx.test.InstrumentationRegistry;
137 import androidx.test.filters.SmallTest;
138 import androidx.test.runner.AndroidJUnit4;
139 
140 import com.android.internal.util.HexDump;
141 import com.android.internal.util.StateMachine;
142 import com.android.net.module.util.ArrayTrackRecord;
143 import com.android.net.module.util.Ipv6Utils;
144 import com.android.net.module.util.structs.PrefixInformationOption;
145 import com.android.net.module.util.structs.RdnssOption;
146 import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
147 import com.android.networkstack.apishim.ConstantsShim;
148 import com.android.networkstack.apishim.common.ShimUtils;
149 import com.android.networkstack.arp.ArpPacket;
150 import com.android.networkstack.metrics.IpProvisioningMetrics;
151 import com.android.networkstack.metrics.NetworkQuirkMetrics;
152 import com.android.networkstack.packets.NeighborAdvertisement;
153 import com.android.server.NetworkObserver;
154 import com.android.server.NetworkObserverRegistry;
155 import com.android.server.NetworkStackService.NetworkStackServiceManager;
156 import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
157 import com.android.testutils.DevSdkIgnoreRule;
158 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
159 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
160 import com.android.testutils.HandlerUtils;
161 import com.android.testutils.TapPacketReader;
162 
163 import org.junit.After;
164 import org.junit.Before;
165 import org.junit.Rule;
166 import org.junit.Test;
167 import org.junit.rules.TestName;
168 import org.junit.runner.RunWith;
169 import org.mockito.ArgumentCaptor;
170 import org.mockito.InOrder;
171 import org.mockito.Mock;
172 import org.mockito.MockitoAnnotations;
173 import org.mockito.Spy;
174 
175 import java.io.BufferedReader;
176 import java.io.File;
177 import java.io.FileDescriptor;
178 import java.io.FileReader;
179 import java.io.IOException;
180 import java.lang.annotation.ElementType;
181 import java.lang.annotation.Retention;
182 import java.lang.annotation.RetentionPolicy;
183 import java.lang.annotation.Target;
184 import java.lang.reflect.Method;
185 import java.net.Inet4Address;
186 import java.net.Inet6Address;
187 import java.net.InetAddress;
188 import java.net.NetworkInterface;
189 import java.nio.ByteBuffer;
190 import java.util.ArrayList;
191 import java.util.Arrays;
192 import java.util.Collection;
193 import java.util.Collections;
194 import java.util.HashMap;
195 import java.util.List;
196 import java.util.Objects;
197 import java.util.Random;
198 import java.util.concurrent.CompletableFuture;
199 import java.util.concurrent.CountDownLatch;
200 import java.util.concurrent.TimeUnit;
201 import java.util.concurrent.atomic.AtomicReference;
202 import java.util.function.Predicate;
203 
204 import kotlin.Lazy;
205 import kotlin.LazyKt;
206 
207 /**
208  * Base class for IpClient tests.
209  *
210  * Tests in this class can either be run with signature permissions, or with root access.
211  */
212 @RunWith(AndroidJUnit4.class)
213 @SmallTest
214 public abstract class IpClientIntegrationTestCommon {
215     private static final int DATA_BUFFER_LEN = 4096;
216     private static final int PACKET_TIMEOUT_MS = 5_000;
217     private static final String TEST_CLUSTER = "some cluster";
218     private static final int TEST_LEASE_DURATION_S = 3_600; // 1 hour
219     private static final int TEST_IPV6_ONLY_WAIT_S = 1_800; // 30 min
220     private static final int TEST_LOWER_IPV6_ONLY_WAIT_S = (int) (MIN_V6ONLY_WAIT_MS / 1000 - 1);
221     private static final int TEST_ZERO_IPV6_ONLY_WAIT_S = 0;
222     private static final long TEST_MAX_IPV6_ONLY_WAIT_S = 0xffffffffL;
223     protected static final String TEST_L2KEY = "some l2key";
224 
225     // TODO: move to NetlinkConstants, NetworkStackConstants, or OsConstants.
226     private static final int IFA_F_STABLE_PRIVACY = 0x800;
227 
228     protected static final long TEST_TIMEOUT_MS = 2_000L;
229 
230     @Rule
231     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
232     @Rule
233     public final TestName mTestNameRule = new TestName();
234 
235     /**
236      * Indicates that a test requires signature permissions to run.
237      *
238      * Such tests can only be run on devices that use known signing keys, so this annotation must be
239      * avoided as much as possible. Consider whether the test can be written to use shell and root
240      * shell permissions, and run against the NetworkStack AIDL interface (IIpClient) instead.
241      */
242     @Retention(RetentionPolicy.RUNTIME)
243     @Target({ElementType.METHOD})
244     private @interface SignatureRequiredTest {
reason()245         String reason();
246     }
247 
248     /**** BEGIN signature required test members ****/
249     // Do not use unless the test *really* cannot be written to exercise IIpClient without mocks.
250     // Tests using the below members must be annotated with @SignatureRequiredTest (otherwise the
251     // members will be null), and can only be run on devices that use known signing keys.
252     // The members could technically be moved to the IpClientIntegrationTest subclass together with
253     // the tests requiring signature permissions, but this would make it harder to follow tests in
254     // multiple classes, and harder to migrate tests between signature required and not required.
255 
256     @Mock private Context mContext;
257     @Mock private ConnectivityManager mCm;
258     @Mock private Resources mResources;
259     @Mock private AlarmManager mAlarm;
260     @Mock private ContentResolver mContentResolver;
261     @Mock private NetworkStackServiceManager mNetworkStackServiceManager;
262     @Mock private IpMemoryStoreService mIpMemoryStoreService;
263     @Mock private PowerManager.WakeLock mTimeoutWakeLock;
264     @Mock protected NetworkStackIpMemoryStore mIpMemoryStore;
265     @Mock private NetworkQuirkMetrics.Dependencies mNetworkQuirkMetricsDeps;
266 
267     @Spy private INetd mNetd;
268     private NetworkObserverRegistry mNetworkObserverRegistry;
269 
270     protected IpClient mIpc;
271     protected Dependencies mDependencies;
272 
273     /***** END signature required test members *****/
274 
275     private IIpClientCallbacks mCb;
276     private IIpClient mIIpClient;
277     private String mIfaceName;
278     private HandlerThread mPacketReaderThread;
279     private Handler mHandler;
280     private TapPacketReader mPacketReader;
281     private FileDescriptor mTapFd;
282     private byte[] mClientMac;
283 
284     private boolean mIsSignatureRequiredTest;
285 
286     // ReadHeads for various packet streams. Cannot be initialized in @Before because ReadHead is
287     // single-thread-only, and AndroidJUnitRunner runs @Before and @Test on different threads.
288     // While it looks like these are created only once per test, they are actually created once per
289     // test method because JUnit recreates a fresh test class instance before every test method.
290     private Lazy<ArrayTrackRecord<byte[]>.ReadHead> mDhcpPacketReadHead =
291             LazyKt.lazy(() -> mPacketReader.getReceivedPackets().newReadHead());
292     private Lazy<ArrayTrackRecord<byte[]>.ReadHead> mArpPacketReadHead =
293             LazyKt.lazy(() -> mPacketReader.getReceivedPackets().newReadHead());
294 
295     // Ethernet header
296     private static final int ETH_HEADER_LEN = 14;
297 
298     // IP header
299     private static final int IPV4_HEADER_LEN = 20;
300     private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
301     private static final int IPV4_DST_ADDR_OFFSET = IPV4_SRC_ADDR_OFFSET + 4;
302 
303     // UDP header
304     private static final int UDP_HEADER_LEN = 8;
305     private static final int UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
306     private static final int UDP_SRC_PORT_OFFSET = UDP_HEADER_OFFSET + 0;
307 
308     // DHCP header
309     private static final int DHCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN
310             + UDP_HEADER_LEN;
311     private static final int DHCP_MESSAGE_OP_CODE_OFFSET = DHCP_HEADER_OFFSET + 0;
312     private static final int DHCP_TRANSACTION_ID_OFFSET = DHCP_HEADER_OFFSET + 4;
313     private static final int DHCP_OPTION_MAGIC_COOKIE_OFFSET = DHCP_HEADER_OFFSET + 236;
314 
315     private static final Inet4Address SERVER_ADDR =
316             (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.100");
317     private static final Inet4Address CLIENT_ADDR =
318             (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.2");
319     private static final Inet4Address CLIENT_ADDR_NEW =
320             (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.3");
321     private static final Inet4Address INADDR_ANY =
322             (Inet4Address) InetAddresses.parseNumericAddress("0.0.0.0");
323     private static final int PREFIX_LENGTH = 24;
324     private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
325     private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
326             SERVER_ADDR, PREFIX_LENGTH);
327     private static final String HOSTNAME = "testhostname";
328     private static final int TEST_DEFAULT_MTU = 1500;
329     private static final int TEST_MIN_MTU = 1280;
330     private static final byte[] SERVER_MAC = new byte[] { 0x00, 0x1A, 0x11, 0x22, 0x33, 0x44 };
331     private static final String TEST_HOST_NAME = "AOSP on Crosshatch";
332     private static final String TEST_HOST_NAME_TRANSLITERATION = "AOSP-on-Crosshatch";
333     private static final String TEST_CAPTIVE_PORTAL_URL = "https://example.com/capportapi";
334     private static final byte[] TEST_HOTSPOT_OUI = new byte[] {
335             (byte) 0x00, (byte) 0x17, (byte) 0xF2
336     };
337     private static final byte TEST_VENDOR_SPECIFIC_TYPE = 0x06;
338 
339     private static final String TEST_DEFAULT_SSID = "test_ssid";
340     private static final String TEST_DEFAULT_BSSID = "00:11:22:33:44:55";
341     private static final String TEST_DHCP_ROAM_SSID = "0001docomo";
342     private static final String TEST_DHCP_ROAM_BSSID = "00:4e:35:17:98:55";
343     private static final String TEST_DHCP_ROAM_L2KEY = "roaming_l2key";
344     private static final String TEST_DHCP_ROAM_CLUSTER = "roaming_cluster";
345     private static final byte[] TEST_AP_OUI = new byte[] { 0x00, 0x1A, 0x11 };
346     private static final byte[] TEST_OEM_OUI = new byte[] {(byte) 0x00, (byte) 0x17, (byte) 0xc3};
347     private static final String TEST_OEM_VENDOR_ID = "vendor-class-identifier";
348     private static final byte[] TEST_OEM_USER_CLASS_INFO = new byte[] {
349             // Instance of User Class: [0]
350             (byte) 0x03, /* UC_Len_0 */ (byte) 0x11, (byte) 0x22, (byte) 0x33,
351             // Instance of User Class: [1]
352             (byte) 0x03, /* UC_Len_1 */ (byte) 0x44, (byte) 0x55, (byte) 0x66,
353     };
354 
355     protected class Dependencies extends IpClient.Dependencies {
356         // Can't use SparseIntArray, it doesn't have an easy way to know if a key is not present.
357         private HashMap<String, Integer> mIntConfigProperties = new HashMap<>();
358         private DhcpClient mDhcpClient;
359         private boolean mIsHostnameConfigurationEnabled;
360         private String mHostname;
361         private boolean mIsInterfaceRecovered;
362 
setHostnameConfiguration(final boolean enable, final String hostname)363         public void setHostnameConfiguration(final boolean enable, final String hostname) {
364             mIsHostnameConfigurationEnabled = enable;
365             mHostname = hostname;
366         }
367 
368         // Enable this flag to simulate the interface has been added back after removing
369         // on the provisioning start. However, the actual tap interface has been removed,
370         // interface parameters query will get null when attempting to restore Interface
371         // MTU. Create a new InterfaceParams instance and return instead just for interface
372         // toggling test case.
simulateInterfaceRecover()373         public void simulateInterfaceRecover() {
374             mIsInterfaceRecovered = true;
375         }
376 
377         @Override
getInterfaceParams(String ifname)378         public InterfaceParams getInterfaceParams(String ifname) {
379             return mIsInterfaceRecovered
380                     ? new InterfaceParams(ifname, 1 /* index */,
381                             MacAddress.fromString("00:11:22:33:44:55"))
382                     : super.getInterfaceParams(ifname);
383         }
384 
385         @Override
getNetd(Context context)386         public INetd getNetd(Context context) {
387             return mNetd;
388         }
389 
390         @Override
getIpMemoryStore(Context context, NetworkStackServiceManager nssManager)391         public NetworkStackIpMemoryStore getIpMemoryStore(Context context,
392                 NetworkStackServiceManager nssManager) {
393             return mIpMemoryStore;
394         }
395 
396         @Override
makeDhcpClient(Context context, StateMachine controller, InterfaceParams ifParams, DhcpClient.Dependencies deps)397         public DhcpClient makeDhcpClient(Context context, StateMachine controller,
398                 InterfaceParams ifParams, DhcpClient.Dependencies deps) {
399             mDhcpClient = DhcpClient.makeDhcpClient(context, controller, ifParams, deps);
400             return mDhcpClient;
401         }
402 
403         @Override
isFeatureEnabled(final Context context, final String name, final boolean defaultEnabled)404         public boolean isFeatureEnabled(final Context context, final String name,
405                 final boolean defaultEnabled) {
406             return IpClientIntegrationTestCommon.this.isFeatureEnabled(name, defaultEnabled);
407         }
408 
409         @Override
getDhcpClientDependencies( NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics)410         public DhcpClient.Dependencies getDhcpClientDependencies(
411                 NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) {
412             return new DhcpClient.Dependencies(ipMemoryStore, metrics) {
413                 @Override
414                 public boolean isFeatureEnabled(final Context context, final String name,
415                         final boolean defaultEnabled) {
416                     return Dependencies.this.isFeatureEnabled(context, name, defaultEnabled);
417                 }
418 
419                 @Override
420                 public int getIntDeviceConfig(final String name, int minimumValue,
421                         int maximumValue, int defaultValue) {
422                     return getDeviceConfigPropertyInt(name, 0 /* default value */);
423                 }
424 
425                 @Override
426                 public PowerManager.WakeLock getWakeLock(final PowerManager powerManager) {
427                     return mTimeoutWakeLock;
428                 }
429 
430                 @Override
431                 public boolean getSendHostnameOption(final Context context) {
432                     return mIsHostnameConfigurationEnabled;
433                 }
434 
435                 @Override
436                 public String getDeviceName(final Context context) {
437                     return mIsHostnameConfigurationEnabled ? mHostname : null;
438                 }
439             };
440         }
441 
442         @Override
getDeviceConfigPropertyInt(String name, int defaultValue)443         public int getDeviceConfigPropertyInt(String name, int defaultValue) {
444             Integer value = mIntConfigProperties.get(name);
445             if (value == null) {
446                 throw new IllegalStateException("Non-mocked device config property " + name);
447             }
448             return value;
449         }
450 
setDeviceConfigProperty(String name, int value)451         public void setDeviceConfigProperty(String name, int value) {
452             mIntConfigProperties.put(name, value);
453         }
454 
455         @Override
getNetworkQuirkMetrics()456         public NetworkQuirkMetrics getNetworkQuirkMetrics() {
457             return new NetworkQuirkMetrics(mNetworkQuirkMetricsDeps);
458         }
459     }
460 
461     @NonNull
462     protected abstract IIpClient makeIIpClient(
463             @NonNull String ifaceName, @NonNull IIpClientCallbacks cb);
464 
465     protected abstract void setFeatureEnabled(String name, boolean enabled);
466 
467     protected abstract boolean isFeatureEnabled(String name, boolean defaultEnabled);
468 
469     protected abstract boolean useNetworkStackSignature();
470 
471     protected abstract NetworkAttributes getStoredNetworkAttributes(String l2Key, long timeout);
472 
473     protected abstract void assertIpMemoryNeverStoreNetworkAttributes(String l2Key, long timeout);
474 
475     protected final boolean testSkipped() {
476         // TODO: split out a test suite for root tests, and fail hard instead of skipping the test
477         // if it is run on devices where TestNetworkStackServiceClient is not supported
478         return !useNetworkStackSignature()
479                 && (mIsSignatureRequiredTest || !TestNetworkStackServiceClient.isSupported());
480     }
481 
482     protected void setDhcpFeatures(final boolean isDhcpLeaseCacheEnabled,
483             final boolean isRapidCommitEnabled, final boolean isDhcpIpConflictDetectEnabled,
484             final boolean isIPv6OnlyPreferredEnabled) {
485         setFeatureEnabled(NetworkStackUtils.DHCP_INIT_REBOOT_VERSION, isDhcpLeaseCacheEnabled);
486         setFeatureEnabled(NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION, isRapidCommitEnabled);
487         setFeatureEnabled(NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION,
488                 isDhcpIpConflictDetectEnabled);
489         setFeatureEnabled(NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION,
490                 isIPv6OnlyPreferredEnabled);
491     }
492 
493     @Before
494     public void setUp() throws Exception {
495         final Method testMethod = IpClientIntegrationTestCommon.class.getMethod(
496                 mTestNameRule.getMethodName());
497         mIsSignatureRequiredTest = testMethod.getAnnotation(SignatureRequiredTest.class) != null;
498         assumeFalse(testSkipped());
499 
500         setUpTapInterface();
501         mCb = mock(IIpClientCallbacks.class);
502 
503         if (useNetworkStackSignature()) {
504             setUpMocks();
505             setUpIpClient();
506         }
507 
508         mIIpClient = makeIIpClient(mIfaceName, mCb);
509     }
510 
511     protected void setUpMocks() throws Exception {
512         MockitoAnnotations.initMocks(this);
513 
514         mDependencies = new Dependencies();
515         when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
516         when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm);
517         when(mContext.getResources()).thenReturn(mResources);
518         when(mContext.getContentResolver()).thenReturn(mContentResolver);
519         when(mNetworkStackServiceManager.getIpMemoryStoreService())
520                 .thenReturn(mIpMemoryStoreService);
521 
522         mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67);
523         mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10);
524         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_PROBE_DELAY_MS, 10);
525         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MIN_MS, 10);
526         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MAX_MS, 20);
527         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_ANNOUNCE_DELAY_MS, 10);
528         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_ANNOUNCE_INTERVAL_MS, 10);
529     }
530 
531     private void awaitIpClientShutdown() throws Exception {
532         verify(mCb, timeout(TEST_TIMEOUT_MS)).onQuit();
533     }
534 
535     @After
536     public void tearDown() throws Exception {
537         if (testSkipped()) return;
538         if (mPacketReader != null) {
539             mHandler.post(() -> mPacketReader.stop()); // Also closes the socket
540             mTapFd = null;
541         }
542         if (mPacketReaderThread != null) {
543             mPacketReaderThread.quitSafely();
544         }
545         mIIpClient.shutdown();
546         awaitIpClientShutdown();
547     }
548 
549     private void setUpTapInterface() throws Exception {
550         final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
551         // Adopt the shell permission identity to create a test TAP interface.
552         inst.getUiAutomation().adoptShellPermissionIdentity();
553 
554         final TestNetworkInterface iface;
555         try {
556             final TestNetworkManager tnm = (TestNetworkManager)
557                     inst.getContext().getSystemService(Context.TEST_NETWORK_SERVICE);
558             iface = tnm.createTapInterface();
559         } finally {
560             // Drop the identity in order to regain the network stack permissions, which the shell
561             // does not have.
562             inst.getUiAutomation().dropShellPermissionIdentity();
563         }
564         mIfaceName = iface.getInterfaceName();
565         mClientMac = getIfaceMacAddr(mIfaceName).toByteArray();
566         mPacketReaderThread = new HandlerThread(
567                 IpClientIntegrationTestCommon.class.getSimpleName());
568         mPacketReaderThread.start();
569         mHandler = mPacketReaderThread.getThreadHandler();
570 
571         // Detach the FileDescriptor from the ParcelFileDescriptor.
572         // Otherwise, the garbage collector might call the ParcelFileDescriptor's finalizer, which
573         // closes the FileDescriptor and destroys our tap interface. An alternative would be to
574         // make the ParcelFileDescriptor or the TestNetworkInterface a class member so they never
575         // go out of scope.
576         mTapFd = new FileDescriptor();
577         mTapFd.setInt$(iface.getFileDescriptor().detachFd());
578         mPacketReader = new TapPacketReader(mHandler, mTapFd, DATA_BUFFER_LEN);
579         mHandler.post(() -> mPacketReader.start());
580     }
581 
582     private MacAddress getIfaceMacAddr(String ifaceName) throws IOException {
583         // InterfaceParams.getByName requires CAP_NET_ADMIN: read the mac address with the shell
584         final String strMacAddr = getOneLineCommandOutput(
585                 "su root cat /sys/class/net/" + ifaceName + "/address");
586         return MacAddress.fromString(strMacAddr);
587     }
588 
589     private String getOneLineCommandOutput(String cmd) throws IOException {
590         try (ParcelFileDescriptor fd = InstrumentationRegistry.getInstrumentation()
591                 .getUiAutomation().executeShellCommand(cmd);
592              BufferedReader reader = new BufferedReader(new FileReader(fd.getFileDescriptor()))) {
593             return reader.readLine();
594         }
595     }
596 
597     private IpClient makeIpClient() throws Exception {
598         IpClient ipc = new IpClient(mContext, mIfaceName, mCb, mNetworkObserverRegistry,
599                 mNetworkStackServiceManager, mDependencies);
600         // Wait for IpClient to enter its initial state. Otherwise, additional setup steps or tests
601         // that mock IpClient's dependencies might interact with those mocks while IpClient is
602         // starting. This would cause UnfinishedStubbingExceptions as mocks cannot be interacted
603         // with while they are being stubbed.
604         HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
605         return ipc;
606     }
607 
608     private void setUpIpClient() throws Exception {
609         final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
610         final IBinder netdIBinder =
611                 (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE);
612         mNetd = spy(INetd.Stub.asInterface(netdIBinder));
613         when(mContext.getSystemService(eq(Context.NETD_SERVICE))).thenReturn(netdIBinder);
614         assertNotNull(mNetd);
615 
616         mNetworkObserverRegistry = new NetworkObserverRegistry();
617         mNetworkObserverRegistry.register(mNetd);
618         mIpc = makeIpClient();
619 
620         // Tell the IpMemoryStore immediately to answer any question about network attributes with a
621         // null response. Otherwise, the DHCP client will wait for two seconds before starting,
622         // while its query to the IpMemoryStore times out.
623         // This does not affect any test that makes the mock memory store return results, because
624         // unlike when(), it is documented that doAnswer() can be called more than once, to change
625         // the behaviour of a mock in the middle of a test.
626         doAnswer(invocation -> {
627             final String l2Key = invocation.getArgument(0);
628             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
629                     .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null);
630             return null;
631         }).when(mIpMemoryStore).retrieveNetworkAttributes(any(), any());
632 
633         disableIpv6ProvisioningDelays();
634     }
635 
636     private <T> T verifyWithTimeout(InOrder inOrder, T t) {
637         if (inOrder != null) {
638             return inOrder.verify(t, timeout(TEST_TIMEOUT_MS));
639         } else {
640             return verify(t, timeout(TEST_TIMEOUT_MS));
641         }
642     }
643 
644     private void expectAlarmCancelled(InOrder inOrder, OnAlarmListener listener) {
645         inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).cancel(eq(listener));
646     }
647 
648     private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, long afterSeconds,
649             Handler handler) {
650         // Allow +/- 3 seconds to prevent flaky tests.
651         final long when = SystemClock.elapsedRealtime() + afterSeconds * 1000;
652         final long min = when - 3 * 1000;
653         final long max = when + 3 * 1000;
654         ArgumentCaptor<OnAlarmListener> captor = ArgumentCaptor.forClass(OnAlarmListener.class);
655         verifyWithTimeout(inOrder, mAlarm).setExact(
656                 anyInt(), longThat(x -> x >= min && x <= max),
657                 contains(tagMatch), captor.capture(), eq(handler));
658         return captor.getValue();
659     }
660 
661     private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, int afterSeconds) {
662         return expectAlarmSet(inOrder, tagMatch, (long) afterSeconds, mIpc.getHandler());
663     }
664 
665     private boolean packetContainsExpectedField(final byte[] packet, final int offset,
666             final byte[] expected) {
667         if (packet.length < offset + expected.length) return false;
668         for (int i = 0; i < expected.length; ++i) {
669             if (packet[offset + i] != expected[i]) return false;
670         }
671         return true;
672     }
673 
674     private boolean isDhcpPacket(final byte[] packet) {
675         final ByteBuffer buffer = ByteBuffer.wrap(packet);
676 
677         // check the packet length
678         if (packet.length < DHCP_HEADER_OFFSET) return false;
679 
680         // check the source port and dest port in UDP header
681         buffer.position(UDP_SRC_PORT_OFFSET);
682         final short udpSrcPort = buffer.getShort();
683         final short udpDstPort = buffer.getShort();
684         if (udpSrcPort != DHCP_CLIENT || udpDstPort != DHCP_SERVER) return false;
685 
686         // check DHCP message type
687         buffer.position(DHCP_MESSAGE_OP_CODE_OFFSET);
688         final byte dhcpOpCode = buffer.get();
689         if (dhcpOpCode != DHCP_BOOTREQUEST) return false;
690 
691         // check DHCP magic cookie
692         buffer.position(DHCP_OPTION_MAGIC_COOKIE_OFFSET);
693         final int dhcpMagicCookie = buffer.getInt();
694         if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) return false;
695 
696         return true;
697     }
698 
699     private ArpPacket parseArpPacketOrNull(final byte[] packet) {
700         try {
701             return ArpPacket.parseArpPacket(packet, packet.length);
702         } catch (ArpPacket.ParseException e) {
703             return null;
704         }
705     }
706 
707     private NeighborAdvertisement parseNeighborAdvertisementOrNull(final byte[] packet) {
708         try {
709             return NeighborAdvertisement.parse(packet, packet.length);
710         } catch (NeighborAdvertisement.ParseException e) {
711             return null;
712         }
713     }
714 
715     private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet,
716             final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
717             final String captivePortalUrl, final Integer ipv6OnlyWaitTime) {
718         return DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
719                 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
720                 clientAddress /* yourIp */, packet.getClientMac(), leaseTimeSec,
721                 NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */,
722                 Collections.singletonList(SERVER_ADDR) /* gateways */,
723                 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
724                 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
725                 false /* metered */, mtu, captivePortalUrl, ipv6OnlyWaitTime);
726     }
727 
728     private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet,
729             final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
730             final String captivePortalUrl) {
731         return buildDhcpOfferPacket(packet, clientAddress, leaseTimeSec, mtu, captivePortalUrl,
732                 null /* ipv6OnlyWaitTime */);
733     }
734 
735     private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet,
736             final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
737             final boolean rapidCommit, final String captivePortalApiUrl,
738             final Integer ipv6OnlyWaitTime) {
739         return DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
740                 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
741                 clientAddress /* yourIp */, CLIENT_ADDR /* requestIp */, packet.getClientMac(),
742                 leaseTimeSec, NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */,
743                 Collections.singletonList(SERVER_ADDR) /* gateways */,
744                 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
745                 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
746                 false /* metered */, mtu, rapidCommit, captivePortalApiUrl, ipv6OnlyWaitTime);
747     }
748 
749     private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet,
750             final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
751             final boolean rapidCommit, final String captivePortalApiUrl) {
752         return buildDhcpAckPacket(packet, clientAddress, leaseTimeSec, mtu, rapidCommit,
753                 captivePortalApiUrl, null /* ipv6OnlyWaitTime */);
754     }
755 
756     private static ByteBuffer buildDhcpNakPacket(final DhcpPacket packet) {
757         return DhcpPacket.buildNakPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
758             SERVER_ADDR /* serverIp */, INADDR_ANY /* relayIp */, packet.getClientMac(),
759             false /* broadcast */, "duplicated request IP address");
760     }
761 
762     private void sendArpReply(final byte[] clientMac) throws IOException {
763         final ByteBuffer packet = ArpPacket.buildArpPacket(clientMac /* dst */,
764                 SERVER_MAC /* src */, INADDR_ANY.getAddress() /* target IP */,
765                 clientMac /* target HW address */, CLIENT_ADDR.getAddress() /* sender IP */,
766                 (short) ARP_REPLY);
767         mPacketReader.sendResponse(packet);
768     }
769 
770     private void sendArpProbe() throws IOException {
771         final ByteBuffer packet = ArpPacket.buildArpPacket(DhcpPacket.ETHER_BROADCAST /* dst */,
772                 SERVER_MAC /* src */, CLIENT_ADDR.getAddress() /* target IP */,
773                 new byte[ETHER_ADDR_LEN] /* target HW address */,
774                 INADDR_ANY.getAddress() /* sender IP */, (short) ARP_REQUEST);
775         mPacketReader.sendResponse(packet);
776     }
777 
778     private void startIpClientProvisioning(final ProvisioningConfiguration cfg) throws Exception {
779         mIIpClient.startProvisioning(cfg.toStableParcelable());
780     }
781 
782     private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
783             final boolean shouldReplyRapidCommitAck, final boolean isPreconnectionEnabled,
784             final boolean isDhcpIpConflictDetectEnabled, final boolean isIPv6OnlyPreferredEnabled,
785             final String displayName, final ScanResultInfo scanResultInfo,
786             final Layer2Information layer2Info) throws Exception {
787         ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
788                 .withoutIpReachabilityMonitor()
789                 .withLayer2Information(layer2Info == null
790                         ? new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
791                               MacAddress.fromString(TEST_DEFAULT_BSSID))
792                         : layer2Info)
793                 .withoutIPv6();
794         if (isPreconnectionEnabled) prov.withPreconnection();
795         if (displayName != null) prov.withDisplayName(displayName);
796         if (scanResultInfo != null) prov.withScanResultInfo(scanResultInfo);
797 
798         setDhcpFeatures(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
799                 isDhcpIpConflictDetectEnabled, isIPv6OnlyPreferredEnabled);
800 
801         startIpClientProvisioning(prov.build());
802         if (!isPreconnectionEnabled) {
803             verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
804         }
805         verify(mCb, never()).onProvisioningFailure(any());
806     }
807 
808     private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
809             final boolean isDhcpRapidCommitEnabled, final boolean isPreconnectionEnabled,
810             final boolean isDhcpIpConflictDetectEnabled, final boolean isIPv6OnlyPreferredEnabled)
811             throws Exception {
812         startIpClientProvisioning(isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled,
813                 isPreconnectionEnabled, isDhcpIpConflictDetectEnabled, isIPv6OnlyPreferredEnabled,
814                 null /* displayName */, null /* ScanResultInfo */, null /* layer2Info */);
815     }
816 
817     private void assertIpMemoryStoreNetworkAttributes(final Integer leaseTimeSec,
818             final long startTime, final int mtu) {
819         final NetworkAttributes na = getStoredNetworkAttributes(TEST_L2KEY, TEST_TIMEOUT_MS);
820         assertNotNull(na);
821         assertEquals(CLIENT_ADDR, na.assignedV4Address);
822         if (leaseTimeSec == null || leaseTimeSec.intValue() == DhcpPacket.INFINITE_LEASE) {
823             assertEquals(Long.MAX_VALUE, na.assignedV4AddressExpiry.longValue());
824         } else {
825             // check the lease expiry's scope
826             final long upperBound = startTime + 7_200_000; // start timestamp + 2h
827             final long lowerBound = startTime + 3_600_000; // start timestamp + 1h
828             final long expiry = na.assignedV4AddressExpiry;
829             assertTrue(upperBound > expiry);
830             assertTrue(lowerBound < expiry);
831         }
832         assertEquals(Collections.singletonList(SERVER_ADDR), na.dnsAddresses);
833         assertEquals(new Integer(mtu), na.mtu);
834     }
835 
836     private void assertIpMemoryNeverStoreNetworkAttributes() {
837         assertIpMemoryNeverStoreNetworkAttributes(TEST_L2KEY, TEST_TIMEOUT_MS);
838     }
839 
840     private void assertHostname(final boolean isHostnameConfigurationEnabled,
841             final String hostname, final String hostnameAfterTransliteration,
842             final List<DhcpPacket> packetList) throws Exception {
843         for (DhcpPacket packet : packetList) {
844             if (!isHostnameConfigurationEnabled || hostname == null) {
845                 assertNoHostname(packet.getHostname());
846             } else {
847                 assertEquals(packet.getHostname(), hostnameAfterTransliteration);
848             }
849         }
850     }
851 
852     private void assertNoHostname(String hostname) {
853         if (ShimUtils.isAtLeastR()) {
854             assertNull(hostname);
855         } else {
856             // Until Q, if no hostname is set, the device falls back to the hostname set via
857             // system property, to avoid breaking Q devices already launched with that setup.
858             assertEquals(SystemProperties.get("net.hostname"), hostname);
859         }
860     }
861 
862     // Helper method to complete DHCP 2-way or 4-way handshake
863     private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
864             final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
865             final boolean shouldReplyRapidCommitAck, final int mtu,
866             final boolean isDhcpIpConflictDetectEnabled,
867             final boolean isIPv6OnlyPreferredEnabled,
868             final String captivePortalApiUrl, final String displayName,
869             final ScanResultInfo scanResultInfo, final Layer2Information layer2Info)
870             throws Exception {
871         startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
872                 false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled,
873                 isIPv6OnlyPreferredEnabled, displayName, scanResultInfo, layer2Info);
874         return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu,
875                 captivePortalApiUrl);
876     }
877 
878     private List<DhcpPacket> handleDhcpPackets(final boolean isSuccessLease,
879             final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu,
880             final String captivePortalApiUrl) throws Exception {
881         final List<DhcpPacket> packetList = new ArrayList<>();
882         DhcpPacket packet;
883         while ((packet = getNextDhcpPacket()) != null) {
884             packetList.add(packet);
885             if (packet instanceof DhcpDiscoverPacket) {
886                 if (shouldReplyRapidCommitAck) {
887                     mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec,
888                               (short) mtu, true /* rapidCommit */, captivePortalApiUrl));
889                 } else {
890                     mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR,
891                             leaseTimeSec, (short) mtu, captivePortalApiUrl));
892                 }
893             } else if (packet instanceof DhcpRequestPacket) {
894                 final ByteBuffer byteBuffer = isSuccessLease
895                         ? buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec, (short) mtu,
896                                 false /* rapidCommit */, captivePortalApiUrl)
897                         : buildDhcpNakPacket(packet);
898                 mPacketReader.sendResponse(byteBuffer);
899             } else {
900                 fail("invalid DHCP packet");
901             }
902 
903             // wait for reply to DHCPOFFER packet if disabling rapid commit option
904             if (shouldReplyRapidCommitAck || !(packet instanceof DhcpDiscoverPacket)) {
905                 return packetList;
906             }
907         }
908         fail("No DHCPREQUEST received on interface");
909         return packetList;
910     }
911 
912     private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
913             final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
914             final boolean isDhcpRapidCommitEnabled, final int mtu,
915             final boolean isDhcpIpConflictDetectEnabled) throws Exception {
916         return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled,
917                 isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled,
918                 false /* isIPv6OnlyPreferredEnabled */,
919                 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */,
920                 null /* layer2Info */);
921     }
922 
923     private List<DhcpPacket> performDhcpHandshake() throws Exception {
924         return performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
925                 false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
926                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
927     }
928 
929     private DhcpPacket getNextDhcpPacket(final long timeout) throws Exception {
930         byte[] packet;
931         while ((packet = mDhcpPacketReadHead.getValue()
932                 .poll(timeout, this::isDhcpPacket)) != null) {
933             final DhcpPacket dhcpPacket = DhcpPacket.decodeFullPacket(packet, packet.length,
934                     ENCAP_L2);
935             if (dhcpPacket != null) return dhcpPacket;
936         }
937         return null;
938     }
939 
940     private DhcpPacket getNextDhcpPacket() throws Exception {
941         final DhcpPacket packet = getNextDhcpPacket(PACKET_TIMEOUT_MS);
942         assertNotNull("No expected DHCP packet received on interface within timeout", packet);
943         return packet;
944     }
945 
946     private DhcpPacket getReplyFromDhcpLease(final NetworkAttributes na, boolean timeout)
947             throws Exception {
948         doAnswer(invocation -> {
949             if (timeout) return null;
950             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
951                     .onNetworkAttributesRetrieved(new Status(SUCCESS), TEST_L2KEY, na);
952             return null;
953         }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any());
954         startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */,
955                 false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
956                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
957         return getNextDhcpPacket();
958     }
959 
960     private void removeTapInterface(final FileDescriptor fd) {
961         try {
962             Os.close(fd);
963         } catch (ErrnoException e) {
964             fail("Fail to close file descriptor: " + e);
965         }
966     }
967 
968     private void verifyAfterIpClientShutdown() throws RemoteException {
969         final LinkProperties emptyLp = new LinkProperties();
970         emptyLp.setInterfaceName(mIfaceName);
971         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(emptyLp);
972     }
973 
974     // Verify IPv4-only provisioning success. No need to verify IPv4 provisioning when below cases
975     // happen:
976     // 1. if there's a failure lease, onProvisioningSuccess() won't be called;
977     // 2. if duplicated IPv4 address detection is enabled, verify TIMEOUT will affect ARP packets
978     //    capture running in other test cases.
979     // 3. if IPv6 is enabled, e.g. withoutIPv6() isn't called when starting provisioning.
980     private void verifyIPv4OnlyProvisioningSuccess(final Collection<InetAddress> addresses)
981             throws Exception {
982         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
983         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
984         LinkProperties lp = captor.getValue();
985         assertNotNull(lp);
986         assertNotEquals(0, lp.getDnsServers().size());
987         assertEquals(addresses.size(), lp.getAddresses().size());
988         assertTrue(lp.getAddresses().containsAll(addresses));
989     }
990 
991     private void doRestoreInitialMtuTest(final boolean shouldChangeMtu,
992             final boolean shouldRemoveTapInterface) throws Exception {
993         final long currentTime = System.currentTimeMillis();
994         int mtu = TEST_DEFAULT_MTU;
995 
996         if (shouldChangeMtu) mtu = TEST_MIN_MTU;
997         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
998                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
999                 mtu, false /* isDhcpIpConflictDetectEnabled */);
1000         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1001         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, mtu);
1002 
1003         if (shouldChangeMtu) {
1004             // Pretend that ConnectivityService set the MTU.
1005             mNetd.interfaceSetMtu(mIfaceName, mtu);
1006             assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), mtu);
1007         }
1008 
1009         // Sometimes, IpClient receives an update with an empty LinkProperties during startup,
1010         // when the link-local address is deleted after interface bringup. Reset expectations
1011         // here to ensure that verifyAfterIpClientShutdown does not fail because it sees two
1012         // empty LinkProperties changes instead of one.
1013         reset(mCb);
1014 
1015         if (shouldRemoveTapInterface) removeTapInterface(mTapFd);
1016         try {
1017             mIpc.shutdown();
1018             awaitIpClientShutdown();
1019             if (shouldRemoveTapInterface) {
1020                 verify(mNetd, never()).interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
1021             } else {
1022                 // Verify that MTU indeed has been restored or not.
1023                 verify(mNetd, times(shouldChangeMtu ? 1 : 0))
1024                         .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
1025             }
1026             verifyAfterIpClientShutdown();
1027         } catch (Exception e) {
1028             fail("Exception should not have been thrown after shutdown: " + e);
1029         }
1030     }
1031 
1032     private DhcpPacket assertDiscoverPacketOnPreconnectionStart() throws Exception {
1033         final ArgumentCaptor<List<Layer2PacketParcelable>> l2PacketList =
1034                 ArgumentCaptor.forClass(List.class);
1035 
1036         verify(mCb, timeout(TEST_TIMEOUT_MS)).onPreconnectionStart(l2PacketList.capture());
1037         final byte[] payload = l2PacketList.getValue().get(0).payload;
1038         DhcpPacket packet = DhcpPacket.decodeFullPacket(payload, payload.length, ENCAP_L2);
1039         assertTrue(packet instanceof DhcpDiscoverPacket);
1040         assertArrayEquals(INADDR_BROADCAST.getAddress(),
1041                 Arrays.copyOfRange(payload, IPV4_DST_ADDR_OFFSET, IPV4_DST_ADDR_OFFSET + 4));
1042         return packet;
1043     }
1044 
1045     private void doIpClientProvisioningWithPreconnectionTest(
1046             final boolean shouldReplyRapidCommitAck, final boolean shouldAbortPreconnection,
1047             final boolean shouldFirePreconnectionTimeout,
1048             final boolean timeoutBeforePreconnectionComplete) throws Exception {
1049         final long currentTime = System.currentTimeMillis();
1050         final ArgumentCaptor<InterfaceConfigurationParcel> ifConfig =
1051                 ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
1052 
1053         startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */,
1054                 shouldReplyRapidCommitAck, true /* isDhcpPreConnectionEnabled */,
1055                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
1056         DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart();
1057         final int preconnDiscoverTransId = packet.getTransactionId();
1058 
1059         if (shouldAbortPreconnection) {
1060             if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) {
1061                 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
1062             }
1063 
1064             mIpc.notifyPreconnectionComplete(false /* abort */);
1065             HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1066 
1067             if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) {
1068                 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
1069             }
1070 
1071             // Either way should get DhcpClient go back to INIT state, and broadcast
1072             // DISCOVER with new transaction ID.
1073             packet = getNextDhcpPacket();
1074             assertTrue(packet instanceof DhcpDiscoverPacket);
1075             assertTrue(packet.getTransactionId() != preconnDiscoverTransId);
1076         } else if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) {
1077             // If timeout fires before success preconnection, DhcpClient will go back to INIT state,
1078             // and broadcast DISCOVER with new transaction ID.
1079             mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
1080             packet = getNextDhcpPacket();
1081             assertTrue(packet instanceof DhcpDiscoverPacket);
1082             assertTrue(packet.getTransactionId() != preconnDiscoverTransId);
1083             // any old response would be ignored due to mismatched transaction ID.
1084         }
1085 
1086         final short mtu = (short) TEST_DEFAULT_MTU;
1087         if (!shouldReplyRapidCommitAck) {
1088             mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR,
1089                     TEST_LEASE_DURATION_S, mtu, null /* captivePortalUrl */));
1090             packet = getNextDhcpPacket();
1091             assertTrue(packet instanceof DhcpRequestPacket);
1092         }
1093         mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, TEST_LEASE_DURATION_S,
1094                 mtu, shouldReplyRapidCommitAck, null /* captivePortalUrl */));
1095 
1096         if (!shouldAbortPreconnection) {
1097             mIpc.notifyPreconnectionComplete(true /* success */);
1098             HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
1099 
1100             // If timeout fires after successful preconnection, right now DhcpClient will have
1101             // already entered BOUND state, the delayed CMD_TIMEOUT command would be ignored. So
1102             // this case should be very rare, because the timeout alarm is cancelled when state
1103             // machine exits from Preconnecting state.
1104             if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) {
1105                 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
1106             }
1107         }
1108         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
1109 
1110         final LinkAddress ipAddress = new LinkAddress(CLIENT_ADDR, PREFIX_LENGTH);
1111         verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetCfg(ifConfig.capture());
1112         assertEquals(ifConfig.getValue().ifName, mIfaceName);
1113         assertEquals(ifConfig.getValue().ipv4Addr, ipAddress.getAddress().getHostAddress());
1114         assertEquals(ifConfig.getValue().prefixLength, PREFIX_LENGTH);
1115         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1116     }
1117 
1118     private ArpPacket getNextArpPacket(final long timeout) throws Exception {
1119         byte[] packet;
1120         while ((packet = mArpPacketReadHead.getValue().poll(timeout, p -> true)) != null) {
1121             final ArpPacket arpPacket = parseArpPacketOrNull(packet);
1122             if (arpPacket != null) return arpPacket;
1123         }
1124         return null;
1125     }
1126 
1127     private ArpPacket getNextArpPacket() throws Exception {
1128         final ArpPacket packet = getNextArpPacket(PACKET_TIMEOUT_MS);
1129         assertNotNull("No expected ARP packet received on interface within timeout", packet);
1130         return packet;
1131     }
1132 
1133     private void assertArpPacket(final ArpPacket packet) {
1134         assertEquals(packet.opCode, ARP_REQUEST);
1135         assertEquals(packet.targetIp, CLIENT_ADDR);
1136         assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac));
1137     }
1138 
1139     private void assertArpProbe(final ArpPacket packet) {
1140         assertArpPacket(packet);
1141         assertEquals(packet.senderIp, INADDR_ANY);
1142     }
1143 
1144     private void assertArpAnnounce(final ArpPacket packet) {
1145         assertArpPacket(packet);
1146         assertEquals(packet.senderIp, CLIENT_ADDR);
1147     }
1148 
1149     private void assertGratuitousARP(final ArpPacket packet) {
1150         assertEquals(packet.opCode, ARP_REPLY);
1151         assertEquals(packet.senderIp, CLIENT_ADDR);
1152         assertEquals(packet.targetIp, CLIENT_ADDR);
1153         assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac));
1154         assertTrue(Arrays.equals(packet.targetHwAddress.toByteArray(), ETHER_BROADCAST));
1155     }
1156 
1157     private void doIpAddressConflictDetectionTest(final boolean causeIpAddressConflict,
1158             final boolean shouldReplyRapidCommitAck, final boolean isDhcpIpConflictDetectEnabled,
1159             final boolean shouldResponseArpReply) throws Exception {
1160         final long currentTime = System.currentTimeMillis();
1161 
1162         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1163                 true /* isDhcpLeaseCacheEnabled */, shouldReplyRapidCommitAck,
1164                 TEST_DEFAULT_MTU, isDhcpIpConflictDetectEnabled);
1165 
1166         // If we receive an ARP packet here, it's guaranteed to be from IP conflict detection,
1167         // because at this time the test interface does not have an IP address and therefore
1168         // won't send ARP for anything.
1169         if (causeIpAddressConflict) {
1170             final ArpPacket arpProbe = getNextArpPacket();
1171             assertArpProbe(arpProbe);
1172 
1173             if (shouldResponseArpReply) {
1174                 sendArpReply(mClientMac);
1175             } else {
1176                 sendArpProbe();
1177             }
1178             final DhcpPacket packet = getNextDhcpPacket();
1179             assertTrue(packet instanceof DhcpDeclinePacket);
1180             assertEquals(packet.mServerIdentifier, SERVER_ADDR);
1181             assertEquals(packet.mRequestedIp, CLIENT_ADDR);
1182 
1183             verify(mCb, never()).onProvisioningFailure(any());
1184             assertIpMemoryNeverStoreNetworkAttributes();
1185         } else if (isDhcpIpConflictDetectEnabled) {
1186             int arpPacketCount = 0;
1187             final List<ArpPacket> packetList = new ArrayList<ArpPacket>();
1188             // Total sent ARP packets should be 5 (3 ARP Probes + 2 ARP Announcements)
1189             ArpPacket packet;
1190             while ((packet = getNextArpPacket(TEST_TIMEOUT_MS)) != null) {
1191                 packetList.add(packet);
1192             }
1193             assertEquals(5, packetList.size());
1194             assertArpProbe(packetList.get(0));
1195             assertArpAnnounce(packetList.get(3));
1196         } else {
1197             verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1198             assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime,
1199                     TEST_DEFAULT_MTU);
1200         }
1201     }
1202 
1203     @Test @SignatureRequiredTest(reason = "InterfaceParams.getByName requires CAP_NET_ADMIN")
1204     public void testInterfaceParams() throws Exception {
1205         InterfaceParams params = InterfaceParams.getByName(mIfaceName);
1206         assertNotNull(params);
1207         assertEquals(mIfaceName, params.name);
1208         assertTrue(params.index > 0);
1209         assertNotNull(params.macAddr);
1210         assertTrue(params.hasMacAddress);
1211 
1212         //  Check interface "lo".
1213         params = InterfaceParams.getByName("lo");
1214         assertNotNull(params);
1215         assertEquals("lo", params.name);
1216         assertTrue(params.index > 0);
1217         assertNotNull(params.macAddr);
1218         assertFalse(params.hasMacAddress);
1219     }
1220 
1221     @Test
1222     public void testDhcpInit() throws Exception {
1223         startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
1224                 false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
1225                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
1226         final DhcpPacket packet = getNextDhcpPacket();
1227         assertTrue(packet instanceof DhcpDiscoverPacket);
1228     }
1229 
1230     @Test
1231     public void testHandleSuccessDhcpLease() throws Exception {
1232         final long currentTime = System.currentTimeMillis();
1233         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1234                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1235                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1236         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1237         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1238     }
1239 
1240     @Test
1241     public void testHandleFailureDhcpLease() throws Exception {
1242         performDhcpHandshake(false /* isSuccessLease */, TEST_LEASE_DURATION_S,
1243                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1244                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1245 
1246         verify(mCb, never()).onProvisioningSuccess(any());
1247         assertIpMemoryNeverStoreNetworkAttributes();
1248     }
1249 
1250     @Test
1251     public void testHandleInfiniteLease() throws Exception {
1252         final long currentTime = System.currentTimeMillis();
1253         performDhcpHandshake(true /* isSuccessLease */, INFINITE_LEASE,
1254                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1255                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1256         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1257         assertIpMemoryStoreNetworkAttributes(INFINITE_LEASE, currentTime, TEST_DEFAULT_MTU);
1258     }
1259 
1260     @Test
1261     public void testHandleNoLease() throws Exception {
1262         final long currentTime = System.currentTimeMillis();
1263         performDhcpHandshake(true /* isSuccessLease */, null /* no lease time */,
1264                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1265                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1266         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1267         assertIpMemoryStoreNetworkAttributes(null, currentTime, TEST_DEFAULT_MTU);
1268     }
1269 
1270     @Test @IgnoreAfter(Build.VERSION_CODES.Q) // INIT-REBOOT is enabled on R.
1271     public void testHandleDisableInitRebootState() throws Exception {
1272         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1273                 false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1274                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1275         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1276         assertIpMemoryNeverStoreNetworkAttributes();
1277     }
1278 
1279     @Test
1280     public void testHandleRapidCommitOption() throws Exception {
1281         final long currentTime = System.currentTimeMillis();
1282         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1283                 true /* isDhcpLeaseCacheEnabled */, true /* shouldReplyRapidCommitAck */,
1284                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1285         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1286         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1287     }
1288 
1289     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1290     public void testDhcpClientStartWithCachedInfiniteLease() throws Exception {
1291         final DhcpPacket packet = getReplyFromDhcpLease(
1292                 new NetworkAttributes.Builder()
1293                     .setAssignedV4Address(CLIENT_ADDR)
1294                     .setAssignedV4AddressExpiry(Long.MAX_VALUE) // lease is always valid
1295                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1296                     .setCluster(TEST_CLUSTER)
1297                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1298                     .build(), false /* timeout */);
1299         assertTrue(packet instanceof DhcpRequestPacket);
1300     }
1301 
1302     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1303     public void testDhcpClientStartWithCachedExpiredLease() throws Exception {
1304         final DhcpPacket packet = getReplyFromDhcpLease(
1305                  new NetworkAttributes.Builder()
1306                     .setAssignedV4Address(CLIENT_ADDR)
1307                     .setAssignedV4AddressExpiry(EXPIRED_LEASE)
1308                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1309                     .setCluster(TEST_CLUSTER)
1310                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1311                     .build(), false /* timeout */);
1312         assertTrue(packet instanceof DhcpDiscoverPacket);
1313     }
1314 
1315     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1316     public void testDhcpClientStartWithNullRetrieveNetworkAttributes() throws Exception {
1317         final DhcpPacket packet = getReplyFromDhcpLease(null /* na */, false /* timeout */);
1318         assertTrue(packet instanceof DhcpDiscoverPacket);
1319     }
1320 
1321     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1322     public void testDhcpClientStartWithTimeoutRetrieveNetworkAttributes() throws Exception {
1323         final DhcpPacket packet = getReplyFromDhcpLease(
1324                 new NetworkAttributes.Builder()
1325                     .setAssignedV4Address(CLIENT_ADDR)
1326                     .setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000)
1327                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1328                     .setCluster(TEST_CLUSTER)
1329                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1330                     .build(), true /* timeout */);
1331         assertTrue(packet instanceof DhcpDiscoverPacket);
1332     }
1333 
1334     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1335     public void testDhcpClientStartWithCachedLeaseWithoutIPAddress() throws Exception {
1336         final DhcpPacket packet = getReplyFromDhcpLease(
1337                 new NetworkAttributes.Builder()
1338                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1339                     .setCluster(TEST_CLUSTER)
1340                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1341                     .build(), false /* timeout */);
1342         assertTrue(packet instanceof DhcpDiscoverPacket);
1343     }
1344 
1345     @Test
1346     public void testDhcpClientRapidCommitEnabled() throws Exception {
1347         startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
1348                 true /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
1349                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
1350         final DhcpPacket packet = getNextDhcpPacket();
1351         assertTrue(packet instanceof DhcpDiscoverPacket);
1352     }
1353 
1354     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
1355     public void testDhcpServerInLinkProperties() throws Exception {
1356         assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q);
1357 
1358         performDhcpHandshake();
1359         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1360         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
1361         assertEquals(SERVER_ADDR, captor.getValue().getDhcpServerAddress());
1362     }
1363 
1364     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1365     public void testRestoreInitialInterfaceMtu() throws Exception {
1366         doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */);
1367     }
1368 
1369     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1370     public void testRestoreInitialInterfaceMtu_WithoutMtuChange() throws Exception {
1371         doRestoreInitialMtuTest(false /* shouldChangeMtu */, false /* shouldRemoveTapInterface */);
1372     }
1373 
1374     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1375     public void testRestoreInitialInterfaceMtu_WithException() throws Exception {
1376         doThrow(new RemoteException("NetdNativeService::interfaceSetMtu")).when(mNetd)
1377                 .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
1378 
1379         doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */);
1380         assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU);
1381     }
1382 
1383     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1384     public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStopping() throws Exception {
1385         doRestoreInitialMtuTest(true /* shouldChangeMtu */, true /* shouldRemoveTapInterface */);
1386     }
1387 
1388     @Test
1389     public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStartingProvisioning()
1390             throws Exception {
1391         removeTapInterface(mTapFd);
1392         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1393                 .withoutIpReachabilityMonitor()
1394                 .withoutIPv6()
1395                 .build();
1396 
1397         startIpClientProvisioning(config);
1398         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
1399         verify(mCb, never()).setNeighborDiscoveryOffload(true);
1400     }
1401 
1402     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1403     public void testRestoreInitialInterfaceMtu_stopIpClientAndRestart() throws Exception {
1404         long currentTime = System.currentTimeMillis();
1405 
1406         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1407                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1408                 TEST_MIN_MTU, false /* isDhcpIpConflictDetectEnabled */);
1409         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1410         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_MIN_MTU);
1411 
1412         // Pretend that ConnectivityService set the MTU.
1413         mNetd.interfaceSetMtu(mIfaceName, TEST_MIN_MTU);
1414         assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU);
1415 
1416         reset(mCb);
1417         reset(mIpMemoryStore);
1418 
1419         // Stop IpClient and then restart provisioning immediately.
1420         mIpc.stop();
1421         currentTime = System.currentTimeMillis();
1422         // Intend to set mtu option to 0, then verify that won't influence interface mtu restore.
1423         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1424                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1425                 0 /* mtu */, false /* isDhcpIpConflictDetectEnabled */);
1426         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1427         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 0 /* mtu */);
1428         assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_DEFAULT_MTU);
1429     }
1430 
1431     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1432     public void testRestoreInitialInterfaceMtu_removeInterfaceAndAddback() throws Exception {
1433         doAnswer(invocation -> {
1434             final LinkProperties lp = invocation.getArgument(0);
1435             assertEquals(lp.getInterfaceName(), mIfaceName);
1436             assertEquals(0, lp.getLinkAddresses().size());
1437             assertEquals(0, lp.getDnsServers().size());
1438 
1439             mDependencies.simulateInterfaceRecover();
1440             return null;
1441         }).when(mCb).onProvisioningFailure(any());
1442 
1443         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1444                 .withoutIpReachabilityMonitor()
1445                 .withoutIPv6()
1446                 .build();
1447 
1448         // Intend to remove the tap interface and force IpClient throw provisioning failure
1449         // due to that interface is not found.
1450         removeTapInterface(mTapFd);
1451         assertNull(InterfaceParams.getByName(mIfaceName));
1452 
1453         startIpClientProvisioning(config);
1454         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
1455 
1456         // Make sure everything queued by this test was processed (e.g. transition to StoppingState
1457         // from ClearingIpAddressState) and tearDown will check if IpClient exits normally or crash.
1458         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1459     }
1460 
1461     private boolean isRouterSolicitation(final byte[] packetBytes) {
1462         ByteBuffer packet = ByteBuffer.wrap(packetBytes);
1463         return packet.getShort(ETHER_TYPE_OFFSET) == (short) ETH_P_IPV6
1464                 && packet.get(ETHER_HEADER_LEN + IPV6_PROTOCOL_OFFSET) == (byte) IPPROTO_ICMPV6
1465                 && packet.get(ETHER_HEADER_LEN + IPV6_HEADER_LEN)
1466                         == (byte) ICMPV6_ROUTER_SOLICITATION;
1467     }
1468 
1469     private boolean isNeighborAdvertisement(final byte[] packetBytes) {
1470         ByteBuffer packet = ByteBuffer.wrap(packetBytes);
1471         return packet.getShort(ETHER_TYPE_OFFSET) == (short) ETH_P_IPV6
1472                 && packet.get(ETHER_HEADER_LEN + IPV6_PROTOCOL_OFFSET) == (byte) IPPROTO_ICMPV6
1473                 && packet.get(ETHER_HEADER_LEN + IPV6_HEADER_LEN)
1474                         == (byte) ICMPV6_NEIGHBOR_ADVERTISEMENT;
1475     }
1476 
1477     private NeighborAdvertisement getNextNeighborAdvertisement() throws ParseException {
1478         final byte[] packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS,
1479                 this::isNeighborAdvertisement);
1480         if (packet == null) return null;
1481 
1482         final NeighborAdvertisement na = parseNeighborAdvertisementOrNull(packet);
1483         assertNotNull("Invalid neighbour advertisement received", na);
1484         return na;
1485     }
1486 
1487     private void waitForRouterSolicitation() throws ParseException {
1488         assertNotNull("No router solicitation received on interface within timeout",
1489                 mPacketReader.popPacket(PACKET_TIMEOUT_MS, this::isRouterSolicitation));
1490     }
1491 
1492     private void sendRouterAdvertisement(boolean waitForRs, short lifetime) throws Exception {
1493         final String dnsServer = "2001:4860:4860::64";
1494         final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64");
1495         ByteBuffer rdnss = buildRdnssOption(3600, dnsServer);
1496         ByteBuffer ra = buildRaPacket(lifetime, pio, rdnss);
1497 
1498         if (waitForRs) {
1499             waitForRouterSolicitation();
1500         }
1501 
1502         mPacketReader.sendResponse(ra);
1503     }
1504 
1505     private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception {
1506         sendRouterAdvertisement(waitForRs, (short) 1800);
1507     }
1508 
1509     private void sendRouterAdvertisementWithZeroLifetime() throws Exception {
1510         sendRouterAdvertisement(false /* waitForRs */, (short) 0);
1511     }
1512 
1513     // TODO: move this and the following method to a common location and use them in ApfTest.
1514     private static ByteBuffer buildPioOption(int valid, int preferred, String prefixString)
1515             throws Exception {
1516         return PrefixInformationOption.build(new IpPrefix(prefixString),
1517                 (byte) (PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS), valid, preferred);
1518     }
1519 
1520     private static ByteBuffer buildRdnssOption(int lifetime, String... servers) throws Exception {
1521         return RdnssOption.build(lifetime, servers);
1522     }
1523 
1524     private static ByteBuffer buildRaPacket(short lifetime, ByteBuffer... options)
1525             throws Exception {
1526         final MacAddress dstMac = MacAddress.fromString("33:33:00:00:00:01");
1527         final MacAddress srcMac = MacAddress.fromString("01:02:03:04:05:06");
1528         final Inet6Address routerLinkLocal =
1529                 (Inet6Address) InetAddresses.parseNumericAddress("fe80::1");
1530 
1531         return Ipv6Utils.buildRaPacket(srcMac, dstMac, routerLinkLocal,
1532                 IPV6_ADDR_ALL_NODES_MULTICAST, (byte) 0 /* M=0, O=0 */, lifetime,
1533                 0 /* Reachable time, unspecified */, 100 /* Retrans time 100ms */,
1534                 options);
1535     }
1536 
1537     private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception {
1538         return buildRaPacket((short) 1800, options);
1539     }
1540 
1541     private void disableIpv6ProvisioningDelays() throws Exception {
1542         // Speed up the test by disabling DAD and removing router_solicitation_delay.
1543         // We don't need to restore the default value because the interface is removed in tearDown.
1544         // TODO: speed up further by not waiting for RS but keying off first IPv6 packet.
1545         mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "router_solicitation_delay", "0");
1546         mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits", "0");
1547     }
1548 
1549     private void assertHasAddressThat(String msg, LinkProperties lp,
1550             Predicate<LinkAddress> condition) {
1551         for (LinkAddress addr : lp.getLinkAddresses()) {
1552             if (condition.test(addr)) {
1553                 return;
1554             }
1555         }
1556         fail(msg + " not found in: " + lp);
1557     }
1558 
1559     private boolean hasFlag(LinkAddress addr, int flag) {
1560         return (addr.getFlags() & flag) == flag;
1561     }
1562 
1563     private boolean isPrivacyAddress(LinkAddress addr) {
1564         return addr.isGlobalPreferred() && hasFlag(addr, IFA_F_TEMPORARY);
1565     }
1566 
1567     private boolean isStablePrivacyAddress(LinkAddress addr) {
1568         // TODO: move away from getting address updates from netd and make this work on Q as well.
1569         final int flag = ShimUtils.isAtLeastR() ? IFA_F_STABLE_PRIVACY : 0;
1570         return addr.isGlobalPreferred() && hasFlag(addr, flag);
1571     }
1572 
1573     private LinkProperties doIpv6OnlyProvisioning() throws Exception {
1574         final InOrder inOrder = inOrder(mCb);
1575         final String dnsServer = "2001:4860:4860::64";
1576         final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64");
1577         final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer);
1578         final ByteBuffer ra = buildRaPacket(pio, rdnss);
1579 
1580         return doIpv6OnlyProvisioning(inOrder, ra);
1581     }
1582 
1583     private LinkProperties doIpv6OnlyProvisioning(InOrder inOrder, ByteBuffer ra) throws Exception {
1584         waitForRouterSolicitation();
1585         mPacketReader.sendResponse(ra);
1586 
1587         // The lambda below needs to write a LinkProperties to a local variable, but lambdas cannot
1588         // write to non-final local variables. So declare a final variable to write to.
1589         final AtomicReference<LinkProperties> lpRef = new AtomicReference<>();
1590 
1591         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1592         verifyWithTimeout(inOrder, mCb).onProvisioningSuccess(captor.capture());
1593         lpRef.set(captor.getValue());
1594 
1595         // Sometimes provisioning completes as soon as the link-local and the stable address appear,
1596         // before the privacy address appears. If so, wait here for the LinkProperties update that
1597         // contains all three address. Otherwise, future calls to verify() might get confused.
1598         if (captor.getValue().getLinkAddresses().size() == 2) {
1599             verifyWithTimeout(inOrder, mCb).onLinkPropertiesChange(argThat(lp -> {
1600                 lpRef.set(lp);
1601                 return lp.getLinkAddresses().size() == 3;
1602             }));
1603         }
1604 
1605         LinkProperties lp = lpRef.get();
1606         assertEquals("Should have 3 IPv6 addresses after provisioning: " + lp,
1607                 3, lp.getLinkAddresses().size());
1608         assertHasAddressThat("link-local address", lp, x -> x.getAddress().isLinkLocalAddress());
1609         assertHasAddressThat("privacy address", lp, this::isPrivacyAddress);
1610         assertHasAddressThat("stable privacy address", lp, this::isStablePrivacyAddress);
1611 
1612         return lp;
1613     }
1614 
1615     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1616     public void testRaRdnss() throws Exception {
1617         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1618                 .withoutIpReachabilityMonitor()
1619                 .withoutIPv4()
1620                 .build();
1621         startIpClientProvisioning(config);
1622 
1623         InOrder inOrder = inOrder(mCb);
1624         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1625 
1626         final String dnsServer = "2001:4860:4860::64";
1627         final String lowlifeDnsServer = "2001:4860:4860::6464";
1628 
1629         final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64");
1630         ByteBuffer rdnss1 = buildRdnssOption(60, lowlifeDnsServer);
1631         ByteBuffer rdnss2 = buildRdnssOption(600, dnsServer);
1632         ByteBuffer ra = buildRaPacket(pio, rdnss1, rdnss2);
1633 
1634         LinkProperties lp = doIpv6OnlyProvisioning(inOrder, ra);
1635 
1636         // Expect that DNS servers with lifetimes below CONFIG_MIN_RDNSS_LIFETIME are not accepted.
1637         assertNotNull(lp);
1638         assertEquals(1, lp.getDnsServers().size());
1639         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
1640 
1641         // If the RDNSS lifetime is above the minimum, the DNS server is accepted.
1642         rdnss1 = buildRdnssOption(68, lowlifeDnsServer);
1643         ra = buildRaPacket(pio, rdnss1, rdnss2);
1644         mPacketReader.sendResponse(ra);
1645         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(captor.capture());
1646         lp = captor.getValue();
1647         assertNotNull(lp);
1648         assertEquals(2, lp.getDnsServers().size());
1649         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
1650         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(lowlifeDnsServer)));
1651 
1652         // Expect that setting RDNSS lifetime of 0 causes loss of provisioning.
1653         rdnss1 = buildRdnssOption(0, dnsServer);
1654         rdnss2 = buildRdnssOption(0, lowlifeDnsServer);
1655         ra = buildRaPacket(pio, rdnss1, rdnss2);
1656         mPacketReader.sendResponse(ra);
1657 
1658         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
1659         lp = captor.getValue();
1660         assertNotNull(lp);
1661         assertEquals(0, lp.getDnsServers().size());
1662         reset(mCb);
1663     }
1664 
1665     private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception {
1666         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(
1667                 argThat(lp -> Objects.equals(expected, lp.getNat64Prefix())));
1668 
1669     }
1670 
1671     private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception {
1672         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(argThat(
1673                 lp -> !Objects.equals(unchanged, lp.getNat64Prefix())));
1674 
1675     }
1676 
1677     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
1678     @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1679     public void testPref64Option() throws Exception {
1680         assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q);
1681 
1682         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1683                 .withoutIpReachabilityMonitor()
1684                 .withoutIPv4()
1685                 .build();
1686         startIpClientProvisioning(config);
1687 
1688         final String dnsServer = "2001:4860:4860::64";
1689         final IpPrefix prefix = new IpPrefix("64:ff9b::/96");
1690         final IpPrefix otherPrefix = new IpPrefix("2001:db8:64::/96");
1691 
1692         final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64");
1693         ByteBuffer rdnss = buildRdnssOption(600, dnsServer);
1694         ByteBuffer pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
1695         ByteBuffer ra = buildRaPacket(pio, rdnss, pref64);
1696 
1697         // The NAT64 prefix might be detected before or after provisioning success.
1698         // Don't test order between these two events.
1699         LinkProperties lp = doIpv6OnlyProvisioning(null /*inOrder*/, ra);
1700         expectAlarmSet(null /*inOrder*/, "PREF64", 600);
1701 
1702         // From now on expect events in order.
1703         InOrder inOrder = inOrder(mCb, mAlarm);
1704         if (lp.getNat64Prefix() != null) {
1705             assertEquals(prefix, lp.getNat64Prefix());
1706         } else {
1707             expectNat64PrefixUpdate(inOrder, prefix);
1708         }
1709 
1710         // Increase the lifetime and expect the prefix not to change.
1711         pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer();
1712         ra = buildRaPacket(pio, rdnss, pref64);
1713         mPacketReader.sendResponse(ra);
1714         OnAlarmListener pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1800);
1715         expectNoNat64PrefixUpdate(inOrder, prefix);
1716         reset(mCb, mAlarm);
1717 
1718         // Reduce the lifetime and expect to reschedule expiry.
1719         pref64 = new StructNdOptPref64(prefix, 1500).toByteBuffer();
1720         ra = buildRaPacket(pio, rdnss, pref64);
1721         mPacketReader.sendResponse(ra);
1722         pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1496);
1723         expectNoNat64PrefixUpdate(inOrder, prefix);
1724         reset(mCb, mAlarm);
1725 
1726         // Withdraw the prefix and expect it to be set to null.
1727         pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer();
1728         ra = buildRaPacket(pio, rdnss, pref64);
1729         mPacketReader.sendResponse(ra);
1730         expectAlarmCancelled(inOrder, pref64Alarm);
1731         expectNat64PrefixUpdate(inOrder, null);
1732         reset(mCb, mAlarm);
1733 
1734         // Re-announce the prefix.
1735         pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
1736         ra = buildRaPacket(pio, rdnss, pref64);
1737         mPacketReader.sendResponse(ra);
1738         expectAlarmSet(inOrder, "PREF64", 600);
1739         expectNat64PrefixUpdate(inOrder, prefix);
1740         reset(mCb, mAlarm);
1741 
1742         // Announce two prefixes. Don't expect any update because if there is already a NAT64
1743         // prefix, any new prefix is ignored.
1744         ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 1200).toByteBuffer();
1745         ra = buildRaPacket(pio, rdnss, pref64, otherPref64);
1746         mPacketReader.sendResponse(ra);
1747         expectAlarmSet(inOrder, "PREF64", 600);
1748         expectNoNat64PrefixUpdate(inOrder, prefix);
1749         reset(mCb, mAlarm);
1750 
1751         // Withdraw the old prefix and continue to announce the new one. Expect a prefix change.
1752         pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer();
1753         ra = buildRaPacket(pio, rdnss, pref64, otherPref64);
1754         mPacketReader.sendResponse(ra);
1755         expectAlarmCancelled(inOrder, pref64Alarm);
1756         // Need a different OnAlarmListener local variable because posting it to the handler in the
1757         // lambda below requires it to be final.
1758         final OnAlarmListener lastAlarm = expectAlarmSet(inOrder, "PREF64", 1200);
1759         expectNat64PrefixUpdate(inOrder, otherPrefix);
1760         reset(mCb, mAlarm);
1761 
1762         // Simulate prefix expiry.
1763         mIpc.getHandler().post(() -> lastAlarm.onAlarm());
1764         expectAlarmCancelled(inOrder, pref64Alarm);
1765         expectNat64PrefixUpdate(inOrder, null);
1766 
1767         // Announce a non-/96 prefix and expect it to be ignored.
1768         IpPrefix invalidPrefix = new IpPrefix("64:ff9b::/64");
1769         pref64 = new StructNdOptPref64(invalidPrefix, 1200).toByteBuffer();
1770         ra = buildRaPacket(pio, rdnss, pref64);
1771         mPacketReader.sendResponse(ra);
1772         expectNoNat64PrefixUpdate(inOrder, invalidPrefix);
1773 
1774         // Re-announce the prefix.
1775         pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
1776         ra = buildRaPacket(pio, rdnss, pref64);
1777         mPacketReader.sendResponse(ra);
1778         final OnAlarmListener clearAlarm = expectAlarmSet(inOrder, "PREF64", 600);
1779         expectNat64PrefixUpdate(inOrder, prefix);
1780         reset(mCb, mAlarm);
1781 
1782         // Check that the alarm is cancelled when IpClient is stopped.
1783         mIpc.stop();
1784         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1785         expectAlarmCancelled(inOrder, clearAlarm);
1786         expectNat64PrefixUpdate(inOrder, null);
1787 
1788         // Check that even if the alarm was already in the message queue while it was cancelled, it
1789         // is safely ignored.
1790         mIpc.getHandler().post(() -> clearAlarm.onAlarm());
1791         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1792     }
1793 
1794     private void addIpAddressAndWaitForIt(final String iface) throws Exception {
1795         final CountDownLatch latch = new CountDownLatch(1);
1796 
1797         final String addr1 = "192.0.2.99";
1798         final String addr2 = "192.0.2.3";
1799         final int prefixLength = 26;
1800 
1801         // Add two IPv4 addresses to the specified interface, and proceed when the NetworkObserver
1802         // has seen the second one. This ensures that every other NetworkObserver registered with
1803         // mNetworkObserverRegistry - in particular, IpClient's - has seen the addition of the first
1804         // address.
1805         final LinkAddress trigger = new LinkAddress(addr2 + "/" + prefixLength);
1806         NetworkObserver observer = new NetworkObserver() {
1807             @Override
1808             public void onInterfaceAddressUpdated(LinkAddress address, String ifName) {
1809                 if (ifName.equals(iface) && address.isSameAddressAs(trigger)) {
1810                     latch.countDown();
1811                 }
1812             }
1813         };
1814 
1815         mNetworkObserverRegistry.registerObserverForNonblockingCallback(observer);
1816         try {
1817             mNetd.interfaceAddAddress(iface, addr1, prefixLength);
1818             mNetd.interfaceAddAddress(iface, addr2, prefixLength);
1819             assertTrue("Trigger IP address " + addr2 + " not seen after " + TEST_TIMEOUT_MS + "ms",
1820                     latch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
1821         } finally {
1822             mNetworkObserverRegistry.unregisterObserver(observer);
1823         }
1824 
1825         // Wait for IpClient to process the addition of the address.
1826         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1827     }
1828 
1829     private void doIPv4OnlyProvisioningAndExitWithLeftAddress() throws Exception {
1830         final long currentTime = System.currentTimeMillis();
1831         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1832                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1833                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1834         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1835         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1836 
1837         // Stop IpClient and expect a final LinkProperties callback with an empty LP.
1838         mIIpClient.stop();
1839         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(
1840                 x -> x.getAddresses().size() == 0
1841                         && x.getRoutes().size() == 0
1842                         && x.getDnsServers().size() == 0));
1843         reset(mCb);
1844 
1845         // Pretend that something else (e.g., Tethering) used the interface and left an IP address
1846         // configured on it. When IpClient starts, it must clear this address before proceeding.
1847         // The address must be noticed before startProvisioning is called, or IpClient will
1848         // immediately declare provisioning success due to the presence of an IPv4 address.
1849         // The address must be IPv4 because IpClient clears IPv6 addresses on startup.
1850         //
1851         // TODO: once IpClient gets IP addresses directly from netlink instead of from netd, it
1852         // may be sufficient to call waitForIdle to see if IpClient has seen the address.
1853         addIpAddressAndWaitForIt(mIfaceName);
1854     }
1855 
1856     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1857     public void testIpClientClearingIpAddressState() throws Exception {
1858         doIPv4OnlyProvisioningAndExitWithLeftAddress();
1859 
1860         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1861                 .withoutIpReachabilityMonitor()
1862                 .build();
1863         startIpClientProvisioning(config);
1864 
1865         sendBasicRouterAdvertisement(true /*waitForRs*/);
1866 
1867         // Check that the IPv4 addresses configured earlier are not in LinkProperties...
1868         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1869         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
1870         assertFalse(captor.getValue().hasIpv4Address());
1871 
1872         // ... or configured on the interface.
1873         InterfaceConfigurationParcel cfg = mNetd.interfaceGetCfg(mIfaceName);
1874         assertEquals("0.0.0.0", cfg.ipv4Addr);
1875     }
1876 
1877     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1878     public void testIpClientClearingIpAddressState_enablePreconnection() throws Exception {
1879         doIPv4OnlyProvisioningAndExitWithLeftAddress();
1880 
1881         // Enter ClearingIpAddressesState to clear the remaining IPv4 addresses and transition to
1882         // PreconnectionState instead of RunningState.
1883         startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
1884                 false /* shouldReplyRapidCommitAck */, true /* isDhcpPreConnectionEnabled */,
1885                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
1886         assertDiscoverPacketOnPreconnectionStart();
1887 
1888         // Force to enter RunningState.
1889         mIpc.notifyPreconnectionComplete(false /* abort */);
1890         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1891     }
1892 
1893     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1894     public void testDhcpClientPreconnection_success() throws Exception {
1895         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1896                 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
1897                 false /* timeoutBeforePreconnectionComplete */);
1898     }
1899 
1900     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1901     public void testDhcpClientPreconnection_SuccessWithoutRapidCommit() throws Exception {
1902         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1903                 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
1904                 false /* timeoutBeforePreconnectionComplete */);
1905     }
1906 
1907     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1908     public void testDhcpClientPreconnection_Abort() throws Exception {
1909         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1910                 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
1911                 false /* timeoutBeforePreconnectionComplete */);
1912     }
1913 
1914     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1915     public void testDhcpClientPreconnection_AbortWithoutRapiCommit() throws Exception {
1916         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1917                 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
1918                 false /* timeoutBeforePreconnectionComplete */);
1919     }
1920 
1921     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1922     public void testDhcpClientPreconnection_TimeoutBeforeAbort() throws Exception {
1923         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1924                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1925                 true /* timeoutBeforePreconnectionComplete */);
1926     }
1927 
1928     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1929     public void testDhcpClientPreconnection_TimeoutBeforeAbortWithoutRapidCommit()
1930             throws Exception {
1931         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1932                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1933                 true /* timeoutBeforePreconnectionComplete */);
1934     }
1935 
1936     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1937     public void testDhcpClientPreconnection_TimeoutafterAbort() throws Exception {
1938         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1939                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1940                 false /* timeoutBeforePreconnectionComplete */);
1941     }
1942 
1943     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1944     public void testDhcpClientPreconnection_TimeoutAfterAbortWithoutRapidCommit() throws Exception {
1945         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1946                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1947                 false /* timeoutBeforePreconnectionComplete */);
1948     }
1949 
1950     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1951     public void testDhcpClientPreconnection_TimeoutBeforeSuccess() throws Exception {
1952         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1953                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1954                 true /* timeoutBeforePreconnectionComplete */);
1955     }
1956 
1957     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1958     public void testDhcpClientPreconnection_TimeoutBeforeSuccessWithoutRapidCommit()
1959             throws Exception {
1960         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1961                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1962                 true /* timeoutBeforePreconnectionComplete */);
1963     }
1964 
1965     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1966     public void testDhcpClientPreconnection_TimeoutAfterSuccess() throws Exception {
1967         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1968                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1969                 false /* timeoutBeforePreconnectionComplete */);
1970     }
1971 
1972     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1973     public void testDhcpClientPreconnection_TimeoutAfterSuccessWithoutRapidCommit()
1974             throws Exception {
1975         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1976                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1977                 false /* timeoutBeforePreconnectionComplete */);
1978     }
1979 
1980     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
1981     public void testDhcpClientPreconnection_WithoutLayer2InfoWhenStartingProv() throws Exception {
1982         // For FILS connection, current bssid (also l2key and cluster) is still null when
1983         // starting provisioning since the L2 link hasn't been established yet. Ensure that
1984         // IpClient won't crash even if initializing an Layer2Info class with null members.
1985         ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
1986                 .withoutIpReachabilityMonitor()
1987                 .withoutIPv6()
1988                 .withPreconnection()
1989                 .withLayer2Information(new Layer2Information(null /* l2key */, null /* cluster */,
1990                         null /* bssid */));
1991 
1992         startIpClientProvisioning(prov.build());
1993         assertDiscoverPacketOnPreconnectionStart();
1994         verify(mCb).setNeighborDiscoveryOffload(true);
1995 
1996         // Force IpClient transition to RunningState from PreconnectionState.
1997         mIIpClient.notifyPreconnectionComplete(false /* success */);
1998         HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
1999         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
2000     }
2001 
2002     @Test
2003     public void testDhcpDecline_conflictByArpReply() throws Exception {
2004         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
2005                 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2006                 true /* shouldResponseArpReply */);
2007     }
2008 
2009     @Test
2010     public void testDhcpDecline_conflictByArpProbe() throws Exception {
2011         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
2012                 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2013                 false /* shouldResponseArpReply */);
2014     }
2015 
2016     @Test
2017     public void testDhcpDecline_EnableFlagWithoutIpConflict() throws Exception {
2018         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
2019                 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2020                 false /* shouldResponseArpReply */);
2021     }
2022 
2023     @Test
2024     public void testDhcpDecline_WithoutIpConflict() throws Exception {
2025         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
2026                 false /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */,
2027                 false /* shouldResponseArpReply */);
2028     }
2029 
2030     @Test
2031     public void testDhcpDecline_WithRapidCommitWithoutIpConflict() throws Exception {
2032         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
2033                 true /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */,
2034                 false /* shouldResponseArpReply */);
2035     }
2036 
2037     @Test
2038     public void testDhcpDecline_WithRapidCommitConflictByArpReply() throws Exception {
2039         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
2040                 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2041                 true /* shouldResponseArpReply */);
2042     }
2043 
2044     @Test
2045     public void testDhcpDecline_WithRapidCommitConflictByArpProbe() throws Exception {
2046         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
2047                 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2048                 false /* shouldResponseArpReply */);
2049     }
2050 
2051     @Test
2052     public void testDhcpDecline_EnableFlagWithRapidCommitWithoutIpConflict() throws Exception {
2053         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
2054                 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
2055                 false /* shouldResponseArpReply */);
2056     }
2057 
2058     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2059     public void testHostname_enableConfig() throws Exception {
2060         mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */,
2061                 TEST_HOST_NAME);
2062 
2063         final long currentTime = System.currentTimeMillis();
2064         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
2065                 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
2066                 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
2067                 false /* isDhcpIpConflictDetectEnabled */);
2068 
2069         assertEquals(2, sentPackets.size());
2070         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2071         assertHostname(true, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
2072         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2073     }
2074 
2075     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2076     public void testHostname_disableConfig() throws Exception {
2077         mDependencies.setHostnameConfiguration(false /* isHostnameConfigurationEnabled */,
2078                 TEST_HOST_NAME);
2079 
2080         final long currentTime = System.currentTimeMillis();
2081         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
2082                 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
2083                 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
2084                 false /* isDhcpIpConflictDetectEnabled */);
2085 
2086         assertEquals(2, sentPackets.size());
2087         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2088         assertHostname(false, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
2089         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2090     }
2091 
2092     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2093     public void testHostname_enableConfigWithNullHostname() throws Exception {
2094         mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */,
2095                 null /* hostname */);
2096 
2097         final long currentTime = System.currentTimeMillis();
2098         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
2099                 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
2100                 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
2101                 false /* isDhcpIpConflictDetectEnabled */);
2102 
2103         assertEquals(2, sentPackets.size());
2104         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2105         assertHostname(true, null /* hostname */, null /* hostnameAfterTransliteration */,
2106                 sentPackets);
2107         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2108     }
2109 
2110     private void runDhcpClientCaptivePortalApiTest(boolean featureEnabled,
2111             boolean serverSendsOption) throws Exception {
2112         startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
2113                 false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */,
2114                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
2115         final DhcpPacket discover = getNextDhcpPacket();
2116         assertTrue(discover instanceof DhcpDiscoverPacket);
2117         assertEquals(featureEnabled, discover.hasRequestedParam(DhcpPacket.DHCP_CAPTIVE_PORTAL));
2118 
2119         // Send Offer and handle Request -> Ack
2120         final String serverSentUrl = serverSendsOption ? TEST_CAPTIVE_PORTAL_URL : null;
2121         mPacketReader.sendResponse(buildDhcpOfferPacket(discover, CLIENT_ADDR,
2122                 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, serverSentUrl));
2123         final int testMtu = 1345;
2124         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2125                 false /* shouldReplyRapidCommitAck */, testMtu, serverSentUrl);
2126 
2127         final Uri expectedUrl = featureEnabled && serverSendsOption
2128                 ? Uri.parse(TEST_CAPTIVE_PORTAL_URL) : null;
2129         // LinkProperties will be updated multiple times. Wait for it to contain DHCP-obtained info,
2130         // such as MTU.
2131         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
2132         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(
2133                 argThat(lp -> lp.getMtu() == testMtu));
2134 
2135         // Ensure that the URL was set as expected in the callbacks.
2136         // Can't verify the URL up to Q as there is no such attribute in LinkProperties.
2137         if (!ShimUtils.isAtLeastR()) return;
2138         verify(mCb, atLeastOnce()).onLinkPropertiesChange(captor.capture());
2139         assertTrue(captor.getAllValues().stream().anyMatch(
2140                 lp -> Objects.equals(expectedUrl, lp.getCaptivePortalApiUrl())));
2141     }
2142 
2143     @Test
2144     public void testDhcpClientCaptivePortalApiEnabled() throws Exception {
2145         // Only run the test on platforms / builds where the API is enabled
2146         assumeTrue(CaptivePortalDataShimImpl.isSupported());
2147         runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, true /* serverSendsOption */);
2148     }
2149 
2150     @Test
2151     public void testDhcpClientCaptivePortalApiEnabled_NoUrl() throws Exception {
2152         // Only run the test on platforms / builds where the API is enabled
2153         assumeTrue(CaptivePortalDataShimImpl.isSupported());
2154         runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, false /* serverSendsOption */);
2155     }
2156 
2157     @Test
2158     public void testDhcpClientCaptivePortalApiDisabled() throws Exception {
2159         // Only run the test on platforms / builds where the API is disabled
2160         assumeFalse(CaptivePortalDataShimImpl.isSupported());
2161         runDhcpClientCaptivePortalApiTest(false /* featureEnabled */, true /* serverSendsOption */);
2162     }
2163 
2164     private ScanResultInfo makeScanResultInfo(final int id, final String ssid,
2165             final String bssid, final byte[] oui, final byte type, final byte[] data) {
2166         final ByteBuffer payload = ByteBuffer.allocate(4 + data.length);
2167         payload.put(oui);
2168         payload.put(type);
2169         payload.put(data);
2170         payload.flip();
2171         final ScanResultInfo.InformationElement ie =
2172                 new ScanResultInfo.InformationElement(id /* IE id */, payload);
2173         return new ScanResultInfo(ssid, bssid, Collections.singletonList(ie));
2174     }
2175 
2176     private ScanResultInfo makeScanResultInfo(final int id, final byte[] oui, final byte type) {
2177         byte[] data = new byte[10];
2178         new Random().nextBytes(data);
2179         return makeScanResultInfo(id, TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID, oui, type, data);
2180     }
2181 
2182     private ScanResultInfo makeScanResultInfo(final String ssid, final String bssid) {
2183         byte[] data = new byte[10];
2184         new Random().nextBytes(data);
2185         return makeScanResultInfo(0xdd, ssid, bssid, TEST_AP_OUI, (byte) 0x06, data);
2186     }
2187 
2188     private void doUpstreamHotspotDetectionTest(final int id, final String displayName,
2189             final String ssid, final byte[] oui, final byte type, final byte[] data,
2190             final boolean expectMetered) throws Exception {
2191         final ScanResultInfo info = makeScanResultInfo(id, ssid, TEST_DEFAULT_BSSID, oui, type,
2192                 data);
2193         final long currentTime = System.currentTimeMillis();
2194         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
2195                 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
2196                 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
2197                 false /* isDhcpIpConflictDetectEnabled */,
2198                 false /* isIPv6OnlyPreferredEnabled */,
2199                 null /* captivePortalApiUrl */, displayName, info /* scanResultInfo */,
2200                 null /* layer2Info */);
2201         assertEquals(2, sentPackets.size());
2202         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2203 
2204         ArgumentCaptor<DhcpResultsParcelable> captor =
2205                 ArgumentCaptor.forClass(DhcpResultsParcelable.class);
2206         verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture());
2207         DhcpResults lease = fromStableParcelable(captor.getValue());
2208         assertNotNull(lease);
2209         assertEquals(lease.getIpAddress().getAddress(), CLIENT_ADDR);
2210         assertEquals(lease.getGateway(), SERVER_ADDR);
2211         assertEquals(1, lease.getDnsServers().size());
2212         assertTrue(lease.getDnsServers().contains(SERVER_ADDR));
2213         assertEquals(lease.getServerAddress(), SERVER_ADDR);
2214         assertEquals(lease.getMtu(), TEST_DEFAULT_MTU);
2215 
2216         if (expectMetered) {
2217             assertEquals(lease.vendorInfo, DhcpPacket.VENDOR_INFO_ANDROID_METERED);
2218         } else {
2219             assertNull(lease.vendorInfo);
2220         }
2221 
2222         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2223     }
2224 
2225     @Test
2226     public void testUpstreamHotspotDetection() throws Exception {
2227         byte[] data = new byte[10];
2228         new Random().nextBytes(data);
2229         doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid",
2230                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2231                 true /* expectMetered */);
2232     }
2233 
2234     @Test
2235     public void testUpstreamHotspotDetection_incorrectIeId() throws Exception {
2236         byte[] data = new byte[10];
2237         new Random().nextBytes(data);
2238         doUpstreamHotspotDetectionTest(0xdc, "\"ssid\"", "ssid",
2239                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2240                 false /* expectMetered */);
2241     }
2242 
2243     @Test
2244     public void testUpstreamHotspotDetection_incorrectOUI() throws Exception {
2245         byte[] data = new byte[10];
2246         new Random().nextBytes(data);
2247         doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid",
2248                 new byte[] { (byte) 0x00, (byte) 0x1A, (byte) 0x11 }, (byte) 0x06, data,
2249                 false /* expectMetered */);
2250     }
2251 
2252     @Test
2253     public void testUpstreamHotspotDetection_incorrectSsid() throws Exception {
2254         byte[] data = new byte[10];
2255         new Random().nextBytes(data);
2256         doUpstreamHotspotDetectionTest(0xdd, "\"another ssid\"", "ssid",
2257                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2258                 false /* expectMetered */);
2259     }
2260 
2261     @Test
2262     public void testUpstreamHotspotDetection_incorrectType() throws Exception {
2263         byte[] data = new byte[10];
2264         new Random().nextBytes(data);
2265         doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid",
2266                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x0a, data,
2267                 false /* expectMetered */);
2268     }
2269 
2270     @Test
2271     public void testUpstreamHotspotDetection_zeroLengthData() throws Exception {
2272         byte[] data = new byte[0];
2273         doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid",
2274                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2275                 true /* expectMetered */);
2276     }
2277 
2278     private void forceLayer2Roaming() throws Exception {
2279         final Layer2InformationParcelable roamingInfo = new Layer2InformationParcelable();
2280         roamingInfo.bssid = MacAddress.fromString(TEST_DHCP_ROAM_BSSID);
2281         roamingInfo.l2Key = TEST_DHCP_ROAM_L2KEY;
2282         roamingInfo.cluster = TEST_DHCP_ROAM_CLUSTER;
2283         mIIpClient.updateLayer2Information(roamingInfo);
2284     }
2285 
2286     private void doDhcpRoamingTest(final boolean hasMismatchedIpAddress, final String displayName,
2287             final MacAddress bssid, final boolean expectRoaming) throws Exception {
2288         long currentTime = System.currentTimeMillis();
2289         final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, bssid);
2290 
2291         doAnswer(invocation -> {
2292             // we don't rely on the Init-Reboot state to renew previous cached IP lease.
2293             // Just return null and force state machine enter INIT state.
2294             final String l2Key = invocation.getArgument(0);
2295             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
2296                     .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null);
2297             return null;
2298         }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any());
2299 
2300         mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */,
2301                 null /* hostname */);
2302         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2303                 true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */,
2304                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */,
2305                 false /* isIPv6OnlyPreferredEnabled */,
2306                 null /* captivePortalApiUrl */, displayName, null /* scanResultInfo */,
2307                 layer2Info);
2308         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2309         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2310 
2311         // simulate the roaming by updating bssid.
2312         forceLayer2Roaming();
2313 
2314         currentTime = System.currentTimeMillis();
2315         reset(mIpMemoryStore);
2316         reset(mCb);
2317         if (!expectRoaming) {
2318             assertIpMemoryNeverStoreNetworkAttributes();
2319             return;
2320         }
2321         // check DHCPREQUEST broadcast sent to renew IP address.
2322         DhcpPacket packet;
2323         packet = getNextDhcpPacket();
2324         assertTrue(packet instanceof DhcpRequestPacket);
2325         assertEquals(packet.mClientIp, CLIENT_ADDR);    // client IP
2326         assertNull(packet.mRequestedIp);                // requested IP option
2327         assertNull(packet.mServerIdentifier);           // server ID
2328 
2329         mPacketReader.sendResponse(buildDhcpAckPacket(packet,
2330                 hasMismatchedIpAddress ? CLIENT_ADDR_NEW : CLIENT_ADDR, TEST_LEASE_DURATION_S,
2331                 (short) TEST_DEFAULT_MTU, false /* rapidcommit */, null /* captivePortalUrl */));
2332         HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
2333         if (hasMismatchedIpAddress) {
2334             // notifyFailure
2335             ArgumentCaptor<DhcpResultsParcelable> captor =
2336                     ArgumentCaptor.forClass(DhcpResultsParcelable.class);
2337             verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture());
2338             DhcpResults lease = fromStableParcelable(captor.getValue());
2339             assertNull(lease);
2340 
2341             // roll back to INIT state.
2342             packet = getNextDhcpPacket();
2343             assertTrue(packet instanceof DhcpDiscoverPacket);
2344         } else {
2345             assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime,
2346                     TEST_DEFAULT_MTU);
2347         }
2348     }
2349 
2350     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2351     public void testDhcpRoaming() throws Exception {
2352         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
2353                 MacAddress.fromString(TEST_DEFAULT_BSSID), true /* expectRoaming */);
2354     }
2355 
2356     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2357     public void testDhcpRoaming_invalidBssid() throws Exception {
2358         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
2359                 MacAddress.fromString(TEST_DHCP_ROAM_BSSID), false /* expectRoaming */);
2360     }
2361 
2362     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2363     public void testDhcpRoaming_nullBssid() throws Exception {
2364         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
2365                 null /* BSSID */, false /* expectRoaming */);
2366     }
2367 
2368     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2369     public void testDhcpRoaming_invalidDisplayName() throws Exception {
2370         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"test-ssid\"" /* display name */,
2371                 MacAddress.fromString(TEST_DEFAULT_BSSID), false /* expectRoaming */);
2372     }
2373 
2374     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2375     public void testDhcpRoaming_mismatchedLeasedIpAddress() throws Exception {
2376         doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
2377                 MacAddress.fromString(TEST_DEFAULT_BSSID), true /* expectRoaming */);
2378     }
2379 
2380     private void performDualStackProvisioning() throws Exception {
2381         final InOrder inOrder = inOrder(mCb);
2382         final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
2383         final String dnsServer = "2001:4860:4860::64";
2384         final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64");
2385         final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer);
2386         final ByteBuffer ra = buildRaPacket(pio, rdnss);
2387 
2388         doIpv6OnlyProvisioning(inOrder, ra);
2389 
2390         // Start IPv4 provisioning and wait until entire provisioning completes.
2391         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2392                 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
2393         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> {
2394             if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false;
2395             lpFuture.complete(x);
2396             return true;
2397         }));
2398 
2399         final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
2400         assertNotNull(lp);
2401         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
2402         assertTrue(lp.getDnsServers().contains(SERVER_ADDR));
2403 
2404         reset(mCb);
2405     }
2406 
2407     private void doDualStackProvisioning(boolean shouldDisableAcceptRa) throws Exception {
2408         when(mCm.shouldAvoidBadWifi()).thenReturn(true);
2409 
2410         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2411                 .withoutIpReachabilityMonitor()
2412                 .build();
2413 
2414         setFeatureEnabled(NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION,
2415                 shouldDisableAcceptRa);
2416         // Enable rapid commit to accelerate DHCP handshake to shorten test duration,
2417         // not strictly necessary.
2418         setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
2419                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
2420         mIpc.startProvisioning(config);
2421 
2422         performDualStackProvisioning();
2423     }
2424 
2425     @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck")
2426     public void testIgnoreIpv6ProvisioningLoss_disableIPv6Stack() throws Exception {
2427         doDualStackProvisioning(false /* shouldDisableAcceptRa */);
2428 
2429         final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
2430 
2431         // Send RA with 0-lifetime and wait until all IPv6-related default route and DNS servers
2432         // have been removed, then verify if there is IPv4-only info left in the LinkProperties.
2433         sendRouterAdvertisementWithZeroLifetime();
2434         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(
2435                 argThat(x -> {
2436                     final boolean isOnlyIPv4Provisioned = (x.getLinkAddresses().size() == 1
2437                             && x.getDnsServers().size() == 1
2438                             && x.getAddresses().get(0) instanceof Inet4Address
2439                             && x.getDnsServers().get(0) instanceof Inet4Address);
2440 
2441                     if (!isOnlyIPv4Provisioned) return false;
2442                     lpFuture.complete(x);
2443                     return true;
2444                 }));
2445         final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
2446         assertNotNull(lp);
2447         assertEquals(lp.getAddresses().get(0), CLIENT_ADDR);
2448         assertEquals(lp.getDnsServers().get(0), SERVER_ADDR);
2449 
2450         final ArgumentCaptor<Integer> quirkEvent = ArgumentCaptor.forClass(Integer.class);
2451         verify(mNetworkQuirkMetricsDeps, timeout(TEST_TIMEOUT_MS)).writeStats(quirkEvent.capture());
2452         assertEquals((long) quirkEvent.getValue(),
2453                 (long) NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST.ordinal());
2454     }
2455 
2456     @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck")
2457     public void testIgnoreIpv6ProvisioningLoss_disableAcceptRa() throws Exception {
2458         doDualStackProvisioning(true /* shouldDisableAcceptRa */);
2459 
2460         final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
2461 
2462         // Send RA with 0-lifetime and wait until all global IPv6 addresses, IPv6-related default
2463         // route and DNS servers have been removed, then verify if there is IPv4-only, IPv6 link
2464         // local address and route to fe80::/64 info left in the LinkProperties.
2465         sendRouterAdvertisementWithZeroLifetime();
2466         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(
2467                 argThat(x -> {
2468                     // Only IPv4 provisioned and IPv6 link-local address
2469                     final boolean isIPv6LinkLocalAndIPv4OnlyProvisioned =
2470                             (x.getLinkAddresses().size() == 2
2471                                     && x.getDnsServers().size() == 1
2472                                     && x.getAddresses().get(0) instanceof Inet4Address
2473                                     && x.getDnsServers().get(0) instanceof Inet4Address);
2474 
2475                     if (!isIPv6LinkLocalAndIPv4OnlyProvisioned) return false;
2476                     lpFuture.complete(x);
2477                     return true;
2478                 }));
2479         final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
2480         assertNotNull(lp);
2481         assertEquals(lp.getAddresses().get(0), CLIENT_ADDR);
2482         assertEquals(lp.getDnsServers().get(0), SERVER_ADDR);
2483         assertTrue(lp.getAddresses().get(1).isLinkLocalAddress());
2484 
2485         reset(mCb);
2486 
2487         // Send an RA to verify that global IPv6 addresses won't be configured on the interface.
2488         sendBasicRouterAdvertisement(false /* waitForRs */);
2489         verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(any());
2490     }
2491 
2492     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2493     public void testDualStackProvisioning() throws Exception {
2494         doDualStackProvisioning(false /* shouldDisableAcceptRa */);
2495 
2496         verify(mCb, never()).onProvisioningFailure(any());
2497     }
2498 
2499     private DhcpPacket verifyDhcpPacketRequestsIPv6OnlyPreferredOption(
2500             Class<? extends DhcpPacket> packetType) throws Exception {
2501         final DhcpPacket packet = getNextDhcpPacket();
2502         assertTrue(packetType.isInstance(packet));
2503         assertTrue(packet.hasRequestedParam(DHCP_IPV6_ONLY_PREFERRED));
2504         return packet;
2505     }
2506 
2507     private void doIPv6OnlyPreferredOptionTest(final Integer ipv6OnlyWaitTime,
2508             final Inet4Address clientAddress) throws Exception {
2509         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2510                 .withoutIpReachabilityMonitor()
2511                 .build();
2512         setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */,
2513                 false /* isDhcpIpConflictDetectEnabled */, true /* isIPv6OnlyPreferredEnabled */);
2514         startIpClientProvisioning(config);
2515 
2516         final DhcpPacket packet =
2517                 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
2518 
2519         // Respond DHCPOFFER with IPv6-Only preferred option and offered address.
2520         mPacketReader.sendResponse(buildDhcpOfferPacket(packet, clientAddress,
2521                 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, null /* captivePortalUrl */,
2522                 ipv6OnlyWaitTime));
2523     }
2524 
2525     private void doDiscoverIPv6OnlyPreferredOptionTest(final int optionSecs,
2526             final long expectedWaitSecs) throws Exception {
2527         doIPv6OnlyPreferredOptionTest(optionSecs, CLIENT_ADDR);
2528         final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT",
2529                 expectedWaitSecs, mDependencies.mDhcpClient.getHandler());
2530         mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm());
2531         // Implicitly check that the client never sent a DHCPREQUEST to request the offered address.
2532         verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
2533     }
2534 
2535     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2536     public void testDiscoverIPv6OnlyPreferredOption() throws Exception {
2537         doDiscoverIPv6OnlyPreferredOptionTest(TEST_IPV6_ONLY_WAIT_S, TEST_IPV6_ONLY_WAIT_S);
2538     }
2539 
2540     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2541     public void testDiscoverIPv6OnlyPreferredOption_LowerIPv6OnlyWait() throws Exception {
2542         doDiscoverIPv6OnlyPreferredOptionTest(TEST_LOWER_IPV6_ONLY_WAIT_S,
2543                 TEST_LOWER_IPV6_ONLY_WAIT_S);
2544     }
2545 
2546     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2547     public void testDiscoverIPv6OnlyPreferredOption_ZeroIPv6OnlyWait() throws Exception {
2548         doDiscoverIPv6OnlyPreferredOptionTest(TEST_ZERO_IPV6_ONLY_WAIT_S,
2549                 TEST_LOWER_IPV6_ONLY_WAIT_S);
2550     }
2551 
2552     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2553     public void testDiscoverIPv6OnlyPreferredOption_MaxIPv6OnlyWait() throws Exception {
2554         doDiscoverIPv6OnlyPreferredOptionTest((int) TEST_MAX_IPV6_ONLY_WAIT_S, 0xffffffffL);
2555     }
2556 
2557     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2558     public void testDiscoverIPv6OnlyPreferredOption_ZeroIPv6OnlyWaitWithOfferedAnyAddress()
2559             throws Exception {
2560         doIPv6OnlyPreferredOptionTest(TEST_ZERO_IPV6_ONLY_WAIT_S, IPV4_ADDR_ANY);
2561 
2562         final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 300,
2563                 mDependencies.mDhcpClient.getHandler());
2564         mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm());
2565 
2566         verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
2567     }
2568 
2569     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2570     public void testDiscoverIPv6OnlyPreferredOption_enabledPreconnection() throws Exception {
2571         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2572                 .withoutIpReachabilityMonitor()
2573                 .withPreconnection()
2574                 .build();
2575 
2576         setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
2577                 false /* isDhcpIpConflictDetectEnabled */, true /* isIPv6OnlyPreferredEnabled */);
2578         startIpClientProvisioning(config);
2579 
2580         final DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart();
2581         verify(mCb).setNeighborDiscoveryOffload(true);
2582 
2583         // Force IpClient transition to RunningState from PreconnectionState.
2584         mIpc.notifyPreconnectionComplete(true /* success */);
2585         HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
2586         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
2587 
2588         // DHCP server SHOULD NOT honor the Rapid-Commit option if the response would
2589         // contain the IPv6-only Preferred option to the client, instead respond with
2590         // a DHCPOFFER.
2591         mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR, TEST_LEASE_DURATION_S,
2592                 (short) TEST_DEFAULT_MTU, null /* captivePortalUrl */, TEST_IPV6_ONLY_WAIT_S));
2593 
2594         final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 1800,
2595                 mDependencies.mDhcpClient.getHandler());
2596         mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm());
2597 
2598         verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class);
2599     }
2600 
2601     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2602     public void testDiscoverIPv6OnlyPreferredOption_NoIPv6OnlyPreferredOption() throws Exception {
2603         doIPv6OnlyPreferredOptionTest(null /* ipv6OnlyWaitTime */, CLIENT_ADDR);
2604 
2605         // The IPv6-only Preferred option SHOULD be included in the Parameter Request List option
2606         // in DHCPREQUEST messages after receiving a DHCPOFFER without this option.
2607         verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpRequestPacket.class);
2608     }
2609 
2610     private void startFromInitRebootStateWithIPv6OnlyPreferredOption(final Integer ipv6OnlyWaitTime,
2611             final long expectedWaitSecs) throws Exception {
2612         doAnswer(invocation -> {
2613             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
2614                     .onNetworkAttributesRetrieved(new Status(SUCCESS), TEST_L2KEY,
2615                             new NetworkAttributes.Builder()
2616                                 .setAssignedV4Address(CLIENT_ADDR)
2617                                 .setAssignedV4AddressExpiry(Long.MAX_VALUE) // lease is always valid
2618                                 .setMtu(new Integer(TEST_DEFAULT_MTU))
2619                                 .setCluster(TEST_CLUSTER)
2620                                 .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
2621                                 .build());
2622             return null;
2623         }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any());
2624 
2625         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2626                 .withoutIpReachabilityMonitor()
2627                 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
2628                           MacAddress.fromString(TEST_DEFAULT_BSSID)))
2629                 .build();
2630 
2631         setDhcpFeatures(true /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */,
2632                 false /* isDhcpIpConflictDetectEnabled */, true /* isIPv6OnlyPreferredEnabled */);
2633         startIpClientProvisioning(config);
2634 
2635         final DhcpPacket packet =
2636                 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpRequestPacket.class);
2637 
2638         // Respond DHCPACK with IPv6-Only preferred option.
2639         mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR,
2640                 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, false /* rapidcommit */,
2641                 null /* captivePortalUrl */, ipv6OnlyWaitTime));
2642 
2643         if (ipv6OnlyWaitTime != null) {
2644             expectAlarmSet(null /* inOrder */, "TIMEOUT", expectedWaitSecs,
2645                     mDependencies.mDhcpClient.getHandler());
2646         }
2647     }
2648 
2649     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2650     public void testRequestIPv6OnlyPreferredOption() throws Exception {
2651         startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_IPV6_ONLY_WAIT_S,
2652                 TEST_IPV6_ONLY_WAIT_S);
2653 
2654         // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
2655         // IPv6-Only preferred option(default value) in the DHCPACK packet.
2656         assertIpMemoryNeverStoreNetworkAttributes();
2657     }
2658 
2659     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2660     public void testRequestIPv6OnlyPreferredOption_LowerIPv6OnlyWait() throws Exception {
2661         startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_LOWER_IPV6_ONLY_WAIT_S,
2662                 TEST_LOWER_IPV6_ONLY_WAIT_S);
2663 
2664         // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
2665         // IPv6-Only preferred option(less than MIN_V6ONLY_WAIT_MS) in the DHCPACK packet.
2666         assertIpMemoryNeverStoreNetworkAttributes();
2667     }
2668 
2669     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2670     public void testRequestIPv6OnlyPreferredOption_ZeroIPv6OnlyWait() throws Exception {
2671         startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_ZERO_IPV6_ONLY_WAIT_S,
2672                 TEST_LOWER_IPV6_ONLY_WAIT_S);
2673 
2674         // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
2675         // IPv6-Only preferred option(0) in the DHCPACK packet.
2676         assertIpMemoryNeverStoreNetworkAttributes();
2677     }
2678 
2679     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2680     public void testRequestIPv6OnlyPreferredOption_MaxIPv6OnlyWait() throws Exception {
2681         startFromInitRebootStateWithIPv6OnlyPreferredOption((int) TEST_MAX_IPV6_ONLY_WAIT_S,
2682                 0xffffffffL);
2683 
2684         // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid
2685         // IPv6-Only preferred option(MAX_UNSIGNED_INTEGER: 0xFFFFFFFF) in the DHCPACK packet.
2686         assertIpMemoryNeverStoreNetworkAttributes();
2687     }
2688 
2689     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
2690     public void testRequestIPv6OnlyPreferredOption_NoIPv6OnlyPreferredOption() throws Exception {
2691         final long currentTime = System.currentTimeMillis();
2692         startFromInitRebootStateWithIPv6OnlyPreferredOption(null /* ipv6OnlyWaitTime */,
2693                 0 /* expectedWaitSecs */);
2694 
2695         // Client processes DHCPACK packet normally and transits to the ConfiguringInterfaceState
2696         // due to the null V6ONLY_WAIT.
2697         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2698     }
2699 
2700     private static int getNumOpenFds() {
2701         return new File("/proc/" + Os.getpid() + "/fd").listFiles().length;
2702     }
2703 
2704     private void shutdownAndRecreateIpClient() throws Exception {
2705         mIpc.shutdown();
2706         awaitIpClientShutdown();
2707         mIpc = makeIpClient();
2708     }
2709 
2710     @Test @SignatureRequiredTest(reason = "Only counts FDs from the current process. TODO: fix")
2711     public void testNoFdLeaks() throws Exception {
2712         // Shut down and restart IpClient once to ensure that any fds that are opened the first
2713         // time it runs do not cause the test to fail.
2714         doDualStackProvisioning(false /* shouldDisableAcceptRa */);
2715         shutdownAndRecreateIpClient();
2716 
2717         // Unfortunately we cannot use a large number of iterations as it would make the test run
2718         // too slowly. On crosshatch-eng each iteration takes ~250ms.
2719         final int iterations = 10;
2720         final int before = getNumOpenFds();
2721         for (int i = 0; i < iterations; i++) {
2722             doDualStackProvisioning(false /* shouldDisableAcceptRa */);
2723             shutdownAndRecreateIpClient();
2724             // The last time this loop runs, mIpc will be shut down in tearDown.
2725         }
2726         final int after = getNumOpenFds();
2727 
2728         // Check that the number of open fds is the same as before.
2729         // If this exact match becomes flaky, we could add some tolerance here (e.g., allow 2-3
2730         // extra fds), since it's likely that any leak would at least leak one FD per loop.
2731         assertEquals("Fd leak after " + iterations + " iterations: ", before, after);
2732     }
2733 
2734     // TODO: delete when DhcpOption is @JavaOnlyImmutable.
2735     private static DhcpOption makeDhcpOption(final byte type, final byte[] value) {
2736         final DhcpOption opt = new DhcpOption();
2737         opt.type = type;
2738         opt.value = value;
2739         return opt;
2740     }
2741 
2742     private static final List<DhcpOption> TEST_OEM_DHCP_OPTIONS = Arrays.asList(
2743             // DHCP_USER_CLASS
2744             makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO),
2745             // DHCP_VENDOR_CLASS_ID
2746             makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes())
2747     );
2748 
2749     private DhcpPacket doCustomizedDhcpOptionsTest(final List<DhcpOption> options,
2750              final ScanResultInfo info) throws Exception {
2751         ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
2752                 .withoutIpReachabilityMonitor()
2753                 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
2754                         MacAddress.fromString(TEST_DEFAULT_BSSID)))
2755                 .withScanResultInfo(info)
2756                 .withDhcpOptions(options)
2757                 .withoutIPv6();
2758 
2759         setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */,
2760                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
2761 
2762         startIpClientProvisioning(prov.build());
2763         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
2764         verify(mCb, never()).onProvisioningFailure(any());
2765 
2766         return getNextDhcpPacket();
2767     }
2768 
2769     @Test
2770     public void testCustomizedDhcpOptions() throws Exception {
2771         final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI,
2772                 (byte) 0x17 /* vendor-specific IE type */);
2773         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
2774 
2775         assertTrue(packet instanceof DhcpDiscoverPacket);
2776         assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
2777         assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO);
2778     }
2779 
2780     @Test
2781     public void testCustomizedDhcpOptions_nullDhcpOptions() throws Exception {
2782         final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI,
2783                 (byte) 0x17 /* vendor-specific IE type */);
2784         final DhcpPacket packet = doCustomizedDhcpOptionsTest(null /* options */, info);
2785 
2786         assertTrue(packet instanceof DhcpDiscoverPacket);
2787         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
2788         assertNull(packet.mUserClass);
2789     }
2790 
2791     @Test
2792     public void testCustomizedDhcpOptions_nullScanResultInfo() throws Exception {
2793         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS,
2794                 null /* scanResultInfo */);
2795 
2796         assertTrue(packet instanceof DhcpDiscoverPacket);
2797         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
2798         assertNull(packet.mUserClass);
2799     }
2800 
2801     @Test
2802     public void testCustomizedDhcpOptions_disallowedOui() throws Exception {
2803         final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */,
2804                 new byte[]{ 0x00, 0x11, 0x22} /* oui */, (byte) 0x17 /* vendor-specific IE type */);
2805         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
2806 
2807         assertTrue(packet instanceof DhcpDiscoverPacket);
2808         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
2809         assertNull(packet.mUserClass);
2810     }
2811 
2812     @Test
2813     public void testCustomizedDhcpOptions_invalidIeId() throws Exception {
2814         final ScanResultInfo info = makeScanResultInfo(0xde /* vendor-specificIE */, TEST_OEM_OUI,
2815                 (byte) 0x17 /* vendor-specific IE type */);
2816         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
2817 
2818         assertTrue(packet instanceof DhcpDiscoverPacket);
2819         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
2820         assertNull(packet.mUserClass);
2821     }
2822 
2823     @Test
2824     public void testCustomizedDhcpOptions_invalidVendorSpecificType() throws Exception {
2825         final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI,
2826                 (byte) 0x10 /* vendor-specific IE type */);
2827         final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info);
2828 
2829         assertTrue(packet instanceof DhcpDiscoverPacket);
2830         assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE));
2831         assertNull(packet.mUserClass);
2832     }
2833 
2834     @Test
2835     public void testCustomizedDhcpOptions_disallowedOption() throws Exception {
2836         final List<DhcpOption> options = Arrays.asList(
2837                 makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()),
2838                 makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO),
2839                 // Option 26: MTU
2840                 makeDhcpOption((byte) 26, HexDump.toByteArray(TEST_DEFAULT_MTU)));
2841         final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI,
2842                 (byte) 0x17 /* vendor-specific IE type */);
2843         final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
2844 
2845         assertTrue(packet instanceof DhcpDiscoverPacket);
2846         assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
2847         assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO);
2848         assertNull(packet.mMtu);
2849     }
2850 
2851     @Test
2852     public void testCustomizedDhcpOptions_disallowedParamRequestOption() throws Exception {
2853         final List<DhcpOption> options = Arrays.asList(
2854                 makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()),
2855                 makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO),
2856                 // NTP_SERVER
2857                 makeDhcpOption((byte) 42, null));
2858         final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI,
2859                 (byte) 0x17 /* vendor-specific IE type */);
2860         final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info);
2861 
2862         assertTrue(packet instanceof DhcpDiscoverPacket);
2863         assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID);
2864         assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO);
2865         assertFalse(packet.hasRequestedParam((byte) 42 /* NTP_SERVER */));
2866     }
2867 
2868     private void assertGratuitousNa(final NeighborAdvertisement na) throws Exception {
2869         final MacAddress etherMulticast =
2870                 NetworkStackUtils.ipv6MulticastToEthernetMulticast(IPV6_ADDR_ALL_ROUTERS_MULTICAST);
2871         final LinkAddress target = new LinkAddress(na.naHdr.target, 64);
2872 
2873         assertEquals(etherMulticast, na.ethHdr.dstMac);
2874         assertEquals(ETH_P_IPV6, na.ethHdr.etherType);
2875         assertEquals(IPPROTO_ICMPV6, na.ipv6Hdr.nextHeader);
2876         assertEquals(0xff, na.ipv6Hdr.hopLimit);
2877         assertTrue(na.ipv6Hdr.srcIp.isLinkLocalAddress());
2878         assertEquals(IPV6_ADDR_ALL_ROUTERS_MULTICAST, na.ipv6Hdr.dstIp);
2879         assertEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, na.icmpv6Hdr.type);
2880         assertEquals(0, na.icmpv6Hdr.code);
2881         assertEquals(0, na.naHdr.flags);
2882         assertTrue(target.isGlobalPreferred());
2883     }
2884 
2885     @Test
2886     public void testGratuitousNaForNewGlobalUnicastAddresses() throws Exception {
2887         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2888                 .withoutIpReachabilityMonitor()
2889                 .withoutIPv4()
2890                 .build();
2891 
2892         setFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION,
2893                 true /* isGratuitousNaEnabled */);
2894         assertTrue(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION, false));
2895         startIpClientProvisioning(config);
2896 
2897         doIpv6OnlyProvisioning();
2898 
2899         final List<NeighborAdvertisement> naList = new ArrayList<>();
2900         NeighborAdvertisement packet;
2901         while ((packet = getNextNeighborAdvertisement()) != null) {
2902             assertGratuitousNa(packet);
2903             naList.add(packet);
2904         }
2905         assertEquals(2, naList.size()); // privacy address and stable privacy address
2906     }
2907 
2908     private void startGratuitousArpAndNaAfterRoamingTest(boolean isGratuitousArpNaRoamingEnabled,
2909             boolean hasIpv4, boolean hasIpv6) throws Exception {
2910         final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
2911                 MacAddress.fromString(TEST_DEFAULT_BSSID));
2912         final ScanResultInfo scanResultInfo =
2913                 makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID);
2914         final ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
2915                 .withoutIpReachabilityMonitor()
2916                 .withLayer2Information(layer2Info)
2917                 .withScanResultInfo(scanResultInfo)
2918                 .withDisplayName("ssid");
2919         if (!hasIpv4) prov.withoutIPv4();
2920         if (!hasIpv6) prov.withoutIPv6();
2921 
2922         // Enable rapid commit to accelerate DHCP handshake to shorten test duration,
2923         // not strictly necessary.
2924         setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
2925                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
2926         if (isGratuitousArpNaRoamingEnabled) {
2927             setFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, true);
2928             assertTrue(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, false));
2929         }
2930         startIpClientProvisioning(prov.build());
2931     }
2932 
2933     private void waitForGratuitousArpAndNaPacket(final List<ArpPacket> arpList,
2934             final List<NeighborAdvertisement> naList) throws Exception {
2935         NeighborAdvertisement na;
2936         ArpPacket garp;
2937         do {
2938             na = getNextNeighborAdvertisement();
2939             if (na != null) {
2940                 assertGratuitousNa(na);
2941                 naList.add(na);
2942             }
2943             garp = getNextArpPacket(TEST_TIMEOUT_MS);
2944             if (garp != null) {
2945                 assertGratuitousARP(garp);
2946                 arpList.add(garp);
2947             }
2948         } while (na != null || garp != null);
2949     }
2950 
2951     @Test
2952     public void testGratuitousArpAndNaAfterRoaming() throws Exception {
2953         startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */,
2954                 true /* hasIpv4 */, true /* hasIpv6 */);
2955         performDualStackProvisioning();
2956         forceLayer2Roaming();
2957 
2958         final List<ArpPacket> arpList = new ArrayList<>();
2959         final List<NeighborAdvertisement> naList = new ArrayList<>();
2960         waitForGratuitousArpAndNaPacket(arpList, naList);
2961         assertEquals(2, naList.size()); // privacy address and stable privacy address
2962         assertEquals(1, arpList.size()); // IPv4 address
2963     }
2964 
2965     @Test
2966     public void testGratuitousArpAndNaAfterRoaming_disableExpFlag() throws Exception {
2967         startGratuitousArpAndNaAfterRoamingTest(false /* isGratuitousArpNaRoamingEnabled */,
2968                 true /* hasIpv6 */, true /* hasIpv6 */);
2969         performDualStackProvisioning();
2970         forceLayer2Roaming();
2971 
2972         final List<ArpPacket> arpList = new ArrayList<>();
2973         final List<NeighborAdvertisement> naList = new ArrayList<>();
2974         waitForGratuitousArpAndNaPacket(arpList, naList);
2975         assertEquals(0, naList.size());
2976         assertEquals(0, arpList.size());
2977     }
2978 
2979     @Test
2980     public void testGratuitousArpAndNaAfterRoaming_IPv6OnlyNetwork() throws Exception {
2981         startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */,
2982                 false /* hasIpv4 */, true /* hasIpv6 */);
2983         doIpv6OnlyProvisioning();
2984         forceLayer2Roaming();
2985 
2986         final List<ArpPacket> arpList = new ArrayList<>();
2987         final List<NeighborAdvertisement> naList = new ArrayList<>();
2988         waitForGratuitousArpAndNaPacket(arpList, naList);
2989         assertEquals(2, naList.size());
2990         assertEquals(0, arpList.size());
2991     }
2992 
2993     @Test
2994     public void testGratuitousArpAndNaAfterRoaming_IPv4OnlyNetwork() throws Exception {
2995         startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */,
2996                 true /* hasIpv4 */, false /* hasIpv6 */);
2997 
2998         // Start IPv4 provisioning and wait until entire provisioning completes.
2999         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
3000                 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
3001         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
3002         forceLayer2Roaming();
3003 
3004         final List<ArpPacket> arpList = new ArrayList<>();
3005         final List<NeighborAdvertisement> naList = new ArrayList<>();
3006         waitForGratuitousArpAndNaPacket(arpList, naList);
3007         assertEquals(0, naList.size());
3008         assertEquals(1, arpList.size());
3009     }
3010 
3011     private void doInitialBssidSetupTest(final Layer2Information layer2Info,
3012             final ScanResultInfo scanResultInfo) throws Exception {
3013         ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
3014                 .withoutIpReachabilityMonitor()
3015                 .withLayer2Information(layer2Info)
3016                 .withScanResultInfo(scanResultInfo)
3017                 .withDisplayName("\"0001docomo\"")
3018                 .withoutIPv6();
3019 
3020         setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* shouldReplyRapidCommitAck */,
3021                 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
3022         startIpClientProvisioning(prov.build());
3023 
3024         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
3025                 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
3026         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
3027         forceLayer2Roaming();
3028     }
3029 
3030     @Test @IgnoreUpTo(Build.VERSION_CODES.R)
3031     public void testSetInitialBssidFromLayer2Info() throws Exception {
3032         final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
3033                 MacAddress.fromString(TEST_DEFAULT_BSSID));
3034 
3035         doInitialBssidSetupTest(layer2Info, null /* scanResultInfo */);
3036 
3037         // Initial BSSID comes from layer2Info, it's different with target roaming bssid,
3038         // then verify that DHCPREQUEST packet is sent after roaming.
3039         final DhcpPacket packet = getNextDhcpPacket();
3040         assertTrue(packet instanceof DhcpRequestPacket);
3041     }
3042 
3043     @Test @IgnoreUpTo(Build.VERSION_CODES.R)
3044     public void testSetInitialBssidFromLayer2Info_NullBssid() throws Exception {
3045         final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
3046                 null /* bssid */);
3047         final ScanResultInfo scanResultInfo =
3048                 makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DHCP_ROAM_BSSID);
3049 
3050         doInitialBssidSetupTest(layer2Info, scanResultInfo);
3051 
3052         // Initial BSSID comes from layer2Info, it's null, no DHCPREQUEST packet
3053         // will be sent after roaming.
3054         final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS);
3055         assertNull(packet);
3056     }
3057 
3058     @Test @IgnoreUpTo(Build.VERSION_CODES.R)
3059     public void testSetInitialBssidFromLayer2Info_SameRoamingBssid() throws Exception {
3060         final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
3061                 MacAddress.fromString(TEST_DHCP_ROAM_BSSID));
3062 
3063         doInitialBssidSetupTest(layer2Info, null /* scanResultInfo */);
3064 
3065         // Initial BSSID comes from layer2Info, it's same with target roaming bssid,
3066         // no DHCPREQUEST packet will be sent after roaming.
3067         final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS);
3068         assertNull(packet);
3069     }
3070 
3071     @Test @IgnoreAfter(Build.VERSION_CODES.R)
3072     public void testSetInitialBssidFromScanResultInfo() throws Exception {
3073         final ScanResultInfo scanResultInfo =
3074                 makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID);
3075 
3076         doInitialBssidSetupTest(null /* layer2Info */, scanResultInfo);
3077 
3078         // Initial BSSID comes from ScanResultInfo, it's different with target roaming bssid,
3079         // then verify that DHCPREQUEST packet is sent after roaming.
3080         final DhcpPacket packet = getNextDhcpPacket();
3081         assertTrue(packet instanceof DhcpRequestPacket);
3082     }
3083 
3084     @Test @IgnoreAfter(Build.VERSION_CODES.R)
3085     public void testSetInitialBssidFromScanResultInfo_SameRoamingBssid() throws Exception {
3086         final ScanResultInfo scanResultInfo =
3087                 makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DHCP_ROAM_BSSID);
3088 
3089         doInitialBssidSetupTest(null /* layer2Info */, scanResultInfo);
3090 
3091         // Initial BSSID comes from ScanResultInfo, it's same with target roaming bssid,
3092         // no DHCPREQUEST packet will be sent after roaming.
3093         final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS);
3094         assertNull(packet);
3095     }
3096 
3097     @Test @IgnoreAfter(Build.VERSION_CODES.R)
3098     public void testSetInitialBssidFromScanResultInfo_BrokenInitialBssid() throws Exception {
3099         final ScanResultInfo scanResultInfo =
3100                 makeScanResultInfo(TEST_DEFAULT_SSID, "00:11:22:33:44:");
3101 
3102         doInitialBssidSetupTest(null /* layer2Info */, scanResultInfo);
3103 
3104         // Initial BSSID comes from ScanResultInfo, it's broken MAC address format and fallback
3105         // to null layer2Info, no DHCPREQUEST packet will be sent after roaming.
3106         final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS);
3107         assertNull(packet);
3108     }
3109 
3110     @Test @IgnoreAfter(Build.VERSION_CODES.R)
3111     public void testSetInitialBssidFromScanResultInfo_BrokenInitialBssidFallback()
3112             throws Exception {
3113         final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
3114                 MacAddress.fromString(TEST_DEFAULT_BSSID));
3115         final ScanResultInfo scanResultInfo =
3116                 makeScanResultInfo(TEST_DEFAULT_SSID, "00:11:22:33:44:");
3117 
3118         doInitialBssidSetupTest(layer2Info, scanResultInfo);
3119 
3120         // Initial BSSID comes from ScanResultInfo, it's broken MAC address format and fallback
3121         // to check layer2Info, then verify DHCPREQUEST packet will be sent after roaming.
3122         final DhcpPacket packet = getNextDhcpPacket();
3123         assertTrue(packet instanceof DhcpRequestPacket);
3124     }
3125 }
3126