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_MAGIC_COOKIE;
23 import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
24 import static android.net.dhcp.DhcpPacket.ENCAP_L2;
25 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
26 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
27 import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable;
28 import static android.net.ipmemorystore.Status.SUCCESS;
29 import static android.system.OsConstants.ETH_P_IPV6;
30 import static android.system.OsConstants.IFA_F_TEMPORARY;
31 import static android.system.OsConstants.IPPROTO_ICMPV6;
32 import static android.system.OsConstants.IPPROTO_TCP;
33 
34 import static com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress;
35 import static com.android.net.module.util.Inet4AddressUtils.getPrefixMaskAsInet4Address;
36 import static com.android.server.util.NetworkStackConstants.ARP_REPLY;
37 import static com.android.server.util.NetworkStackConstants.ARP_REQUEST;
38 import static com.android.server.util.NetworkStackConstants.ETHER_ADDR_LEN;
39 import static com.android.server.util.NetworkStackConstants.ETHER_HEADER_LEN;
40 import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV6;
41 import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_OFFSET;
42 import static com.android.server.util.NetworkStackConstants.ICMPV6_CHECKSUM_OFFSET;
43 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR;
44 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO;
45 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_RDNSS;
46 import static com.android.server.util.NetworkStackConstants.ICMPV6_RA_HEADER_LEN;
47 import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
48 import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
49 import static com.android.server.util.NetworkStackConstants.IPV6_HEADER_LEN;
50 import static com.android.server.util.NetworkStackConstants.IPV6_LEN_OFFSET;
51 import static com.android.server.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
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.doAnswer;
70 import static org.mockito.Mockito.doThrow;
71 import static org.mockito.Mockito.eq;
72 import static org.mockito.Mockito.inOrder;
73 import static org.mockito.Mockito.never;
74 import static org.mockito.Mockito.reset;
75 import static org.mockito.Mockito.spy;
76 import static org.mockito.Mockito.timeout;
77 import static org.mockito.Mockito.times;
78 import static org.mockito.Mockito.verify;
79 import static org.mockito.Mockito.when;
80 
81 import android.app.AlarmManager;
82 import android.app.AlarmManager.OnAlarmListener;
83 import android.app.Instrumentation;
84 import android.content.ContentResolver;
85 import android.content.Context;
86 import android.content.res.Resources;
87 import android.net.ConnectivityManager;
88 import android.net.DhcpResults;
89 import android.net.DhcpResultsParcelable;
90 import android.net.INetd;
91 import android.net.InetAddresses;
92 import android.net.InterfaceConfigurationParcel;
93 import android.net.IpPrefix;
94 import android.net.Layer2InformationParcelable;
95 import android.net.Layer2PacketParcelable;
96 import android.net.LinkAddress;
97 import android.net.LinkProperties;
98 import android.net.MacAddress;
99 import android.net.NetworkStackIpMemoryStore;
100 import android.net.TestNetworkInterface;
101 import android.net.TestNetworkManager;
102 import android.net.Uri;
103 import android.net.dhcp.DhcpClient;
104 import android.net.dhcp.DhcpDeclinePacket;
105 import android.net.dhcp.DhcpDiscoverPacket;
106 import android.net.dhcp.DhcpPacket;
107 import android.net.dhcp.DhcpPacket.ParseException;
108 import android.net.dhcp.DhcpRequestPacket;
109 import android.net.ipmemorystore.NetworkAttributes;
110 import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
111 import android.net.ipmemorystore.Status;
112 import android.net.netlink.StructNdOptPref64;
113 import android.net.shared.Layer2Information;
114 import android.net.shared.ProvisioningConfiguration;
115 import android.net.shared.ProvisioningConfiguration.ScanResultInfo;
116 import android.net.util.InterfaceParams;
117 import android.net.util.IpUtils;
118 import android.net.util.NetworkStackUtils;
119 import android.os.Build;
120 import android.os.Handler;
121 import android.os.HandlerThread;
122 import android.os.IBinder;
123 import android.os.PowerManager;
124 import android.os.RemoteException;
125 import android.os.SystemClock;
126 import android.os.SystemProperties;
127 import android.system.ErrnoException;
128 import android.system.Os;
129 
130 import androidx.test.InstrumentationRegistry;
131 import androidx.test.filters.SmallTest;
132 import androidx.test.runner.AndroidJUnit4;
133 
134 import com.android.internal.util.StateMachine;
135 import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
136 import com.android.networkstack.apishim.ConstantsShim;
137 import com.android.networkstack.apishim.common.ShimUtils;
138 import com.android.networkstack.arp.ArpPacket;
139 import com.android.networkstack.metrics.IpProvisioningMetrics;
140 import com.android.server.NetworkObserver;
141 import com.android.server.NetworkObserverRegistry;
142 import com.android.server.NetworkStackService.NetworkStackServiceManager;
143 import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
144 import com.android.testutils.DevSdkIgnoreRule;
145 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
146 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
147 import com.android.testutils.HandlerUtilsKt;
148 import com.android.testutils.TapPacketReader;
149 
150 import org.junit.After;
151 import org.junit.Before;
152 import org.junit.Rule;
153 import org.junit.Test;
154 import org.junit.runner.RunWith;
155 import org.mockito.ArgumentCaptor;
156 import org.mockito.InOrder;
157 import org.mockito.Mock;
158 import org.mockito.MockitoAnnotations;
159 import org.mockito.Spy;
160 
161 import java.io.FileDescriptor;
162 import java.io.IOException;
163 import java.net.Inet4Address;
164 import java.net.InetAddress;
165 import java.net.NetworkInterface;
166 import java.nio.ByteBuffer;
167 import java.util.ArrayList;
168 import java.util.Arrays;
169 import java.util.Collection;
170 import java.util.Collections;
171 import java.util.HashMap;
172 import java.util.List;
173 import java.util.Objects;
174 import java.util.Random;
175 import java.util.concurrent.CompletableFuture;
176 import java.util.concurrent.CountDownLatch;
177 import java.util.concurrent.TimeUnit;
178 import java.util.concurrent.atomic.AtomicReference;
179 import java.util.function.Predicate;
180 
181 /**
182  * Tests for IpClient.
183  */
184 @RunWith(AndroidJUnit4.class)
185 @SmallTest
186 public class IpClientIntegrationTest {
187     private static final int DATA_BUFFER_LEN = 4096;
188     private static final int PACKET_TIMEOUT_MS = 5_000;
189     private static final int TEST_TIMEOUT_MS = 400;
190     private static final String TEST_L2KEY = "some l2key";
191     private static final String TEST_CLUSTER = "some cluster";
192     private static final int TEST_LEASE_DURATION_S = 3_600; // 1 hour
193 
194     // TODO: move to NetlinkConstants, NetworkStackConstants, or OsConstants.
195     private static final int IFA_F_STABLE_PRIVACY = 0x800;
196 
197     @Rule
198     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
199 
200     @Mock private Context mContext;
201     @Mock private ConnectivityManager mCm;
202     @Mock private Resources mResources;
203     @Mock private IIpClientCallbacks mCb;
204     @Mock private AlarmManager mAlarm;
205     @Mock private ContentResolver mContentResolver;
206     @Mock private NetworkStackServiceManager mNetworkStackServiceManager;
207     @Mock private NetworkStackIpMemoryStore mIpMemoryStore;
208     @Mock private IpMemoryStoreService mIpMemoryStoreService;
209     @Mock private PowerManager.WakeLock mTimeoutWakeLock;
210 
211     @Spy private INetd mNetd;
212 
213     private String mIfaceName;
214     private NetworkObserverRegistry mNetworkObserverRegistry;
215     private HandlerThread mPacketReaderThread;
216     private Handler mHandler;
217     private TapPacketReader mPacketReader;
218     private FileDescriptor mTapFd;
219     private IpClient mIpc;
220     private Dependencies mDependencies;
221     private byte[] mClientMac;
222 
223     // Ethernet header
224     private static final int ETH_HEADER_LEN = 14;
225 
226     // IP header
227     private static final int IPV4_HEADER_LEN = 20;
228     private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
229     private static final int IPV4_DST_ADDR_OFFSET = IPV4_SRC_ADDR_OFFSET + 4;
230 
231     // UDP header
232     private static final int UDP_HEADER_LEN = 8;
233     private static final int UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
234     private static final int UDP_SRC_PORT_OFFSET = UDP_HEADER_OFFSET + 0;
235 
236     // DHCP header
237     private static final int DHCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN
238             + UDP_HEADER_LEN;
239     private static final int DHCP_MESSAGE_OP_CODE_OFFSET = DHCP_HEADER_OFFSET + 0;
240     private static final int DHCP_TRANSACTION_ID_OFFSET = DHCP_HEADER_OFFSET + 4;
241     private static final int DHCP_OPTION_MAGIC_COOKIE_OFFSET = DHCP_HEADER_OFFSET + 236;
242 
243     private static final Inet4Address SERVER_ADDR =
244             (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.100");
245     private static final Inet4Address CLIENT_ADDR =
246             (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.2");
247     private static final Inet4Address CLIENT_ADDR_NEW =
248             (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.3");
249     private static final Inet4Address INADDR_ANY =
250             (Inet4Address) InetAddresses.parseNumericAddress("0.0.0.0");
251     private static final int PREFIX_LENGTH = 24;
252     private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
253     private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
254             SERVER_ADDR, PREFIX_LENGTH);
255     private static final String HOSTNAME = "testhostname";
256     private static final int TEST_DEFAULT_MTU = 1500;
257     private static final int TEST_MIN_MTU = 1280;
258     private static final byte[] SERVER_MAC = new byte[] { 0x00, 0x1A, 0x11, 0x22, 0x33, 0x44 };
259     private static final String TEST_HOST_NAME = "AOSP on Crosshatch";
260     private static final String TEST_HOST_NAME_TRANSLITERATION = "AOSP-on-Crosshatch";
261     private static final String TEST_CAPTIVE_PORTAL_URL = "https://example.com/capportapi";
262     private static final byte[] TEST_HOTSPOT_OUI = new byte[] {
263             (byte) 0x00, (byte) 0x17, (byte) 0xF2
264     };
265     private static final byte TEST_VENDOR_SPECIFIC_TYPE = 0x06;
266 
267     private static final String TEST_DEFAULT_SSID = "test_ssid";
268     private static final String TEST_DEFAULT_BSSID = "00:11:22:33:44:55";
269     private static final String TEST_DHCP_ROAM_SSID = "0001docomo";
270     private static final String TEST_DHCP_ROAM_BSSID = "00:4e:35:17:98:55";
271     private static final String TEST_DHCP_ROAM_L2KEY = "roaming_l2key";
272     private static final String TEST_DHCP_ROAM_CLUSTER = "roaming_cluster";
273     private static final byte[] TEST_AP_OUI = new byte[] { 0x00, 0x1A, 0x11 };
274 
275     private class Dependencies extends IpClient.Dependencies {
276         private boolean mIsDhcpLeaseCacheEnabled;
277         private boolean mIsDhcpRapidCommitEnabled;
278         private boolean mIsDhcpIpConflictDetectEnabled;
279         // Can't use SparseIntArray, it doesn't have an easy way to know if a key is not present.
280         private HashMap<String, Integer> mIntConfigProperties = new HashMap<>();
281         private DhcpClient mDhcpClient;
282         private boolean mIsHostnameConfigurationEnabled;
283         private String mHostname;
284 
setDhcpLeaseCacheEnabled(final boolean enable)285         public void setDhcpLeaseCacheEnabled(final boolean enable) {
286             mIsDhcpLeaseCacheEnabled = enable;
287         }
288 
setDhcpRapidCommitEnabled(final boolean enable)289         public void setDhcpRapidCommitEnabled(final boolean enable) {
290             mIsDhcpRapidCommitEnabled = enable;
291         }
292 
setDhcpIpConflictDetectEnabled(final boolean enable)293         public void setDhcpIpConflictDetectEnabled(final boolean enable) {
294             mIsDhcpIpConflictDetectEnabled = enable;
295         }
296 
setHostnameConfiguration(final boolean enable, final String hostname)297         public void setHostnameConfiguration(final boolean enable, final String hostname) {
298             mIsHostnameConfigurationEnabled = enable;
299             mHostname = hostname;
300         }
301 
302         @Override
getNetd(Context context)303         public INetd getNetd(Context context) {
304             return mNetd;
305         }
306 
307         @Override
getIpMemoryStore(Context context, NetworkStackServiceManager nssManager)308         public NetworkStackIpMemoryStore getIpMemoryStore(Context context,
309                 NetworkStackServiceManager nssManager) {
310             return mIpMemoryStore;
311         }
312 
313         @Override
makeDhcpClient(Context context, StateMachine controller, InterfaceParams ifParams, DhcpClient.Dependencies deps)314         public DhcpClient makeDhcpClient(Context context, StateMachine controller,
315                 InterfaceParams ifParams, DhcpClient.Dependencies deps) {
316             mDhcpClient = DhcpClient.makeDhcpClient(context, controller, ifParams, deps);
317             return mDhcpClient;
318         }
319 
320         @Override
getDhcpClientDependencies( NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics)321         public DhcpClient.Dependencies getDhcpClientDependencies(
322                 NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) {
323             return new DhcpClient.Dependencies(ipMemoryStore, metrics) {
324                 @Override
325                 public boolean isFeatureEnabled(final Context context, final String name,
326                         final boolean defaultEnabled) {
327                     switch (name) {
328                         case NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION:
329                             return mIsDhcpRapidCommitEnabled;
330                         case NetworkStackUtils.DHCP_INIT_REBOOT_VERSION:
331                             return mIsDhcpLeaseCacheEnabled;
332                         case NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION:
333                             return mIsDhcpIpConflictDetectEnabled;
334                         default:
335                             fail("Invalid experiment flag: " + name);
336                             return false;
337                     }
338                 }
339 
340                 @Override
341                 public int getIntDeviceConfig(final String name, int minimumValue,
342                         int maximumValue, int defaultValue) {
343                     return getDeviceConfigPropertyInt(name, 0 /* default value */);
344                 }
345 
346                 @Override
347                 public PowerManager.WakeLock getWakeLock(final PowerManager powerManager) {
348                     return mTimeoutWakeLock;
349                 }
350 
351                 @Override
352                 public boolean getSendHostnameOption(final Context context) {
353                     return mIsHostnameConfigurationEnabled;
354                 }
355 
356                 @Override
357                 public String getDeviceName(final Context context) {
358                     return mIsHostnameConfigurationEnabled ? mHostname : null;
359                 }
360             };
361         }
362 
363         @Override
364         public int getDeviceConfigPropertyInt(String name, int defaultValue) {
365             Integer value = mIntConfigProperties.get(name);
366             if (value == null) {
367                 throw new IllegalStateException("Non-mocked device config property " + name);
368             }
369             return value;
370         }
371 
372         public void setDeviceConfigProperty(String name, int value) {
373             mIntConfigProperties.put(name, value);
374         }
375     }
376 
377     @Before
378     public void setUp() throws Exception {
379         MockitoAnnotations.initMocks(this);
380 
381         mDependencies = new Dependencies();
382         when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
383         when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm);
384         when(mContext.getResources()).thenReturn(mResources);
385         when(mContext.getContentResolver()).thenReturn(mContentResolver);
386         when(mNetworkStackServiceManager.getIpMemoryStoreService())
387                 .thenReturn(mIpMemoryStoreService);
388 
389         mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67);
390         mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10);
391         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_PROBE_DELAY_MS, 10);
392         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MIN_MS, 10);
393         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MAX_MS, 20);
394         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_ANNOUNCE_DELAY_MS, 10);
395         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_ANNOUNCE_INTERVAL_MS, 10);
396 
397         setUpTapInterface();
398         setUpIpClient();
399     }
400 
401     private void awaitIpClientShutdown() throws Exception {
402         verify(mCb, timeout(TEST_TIMEOUT_MS)).onQuit();
403     }
404 
405     @After
406     public void tearDown() throws Exception {
407         if (mPacketReader != null) {
408             mHandler.post(() -> mPacketReader.stop()); // Also closes the socket
409             mTapFd = null;
410         }
411         if (mPacketReaderThread != null) {
412             mPacketReaderThread.quitSafely();
413         }
414         mIpc.shutdown();
415         awaitIpClientShutdown();
416     }
417 
418     private void setUpTapInterface() {
419         final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
420         // Adopt the shell permission identity to create a test TAP interface.
421         inst.getUiAutomation().adoptShellPermissionIdentity();
422 
423         final TestNetworkInterface iface;
424         try {
425             final TestNetworkManager tnm = (TestNetworkManager)
426                     inst.getContext().getSystemService(Context.TEST_NETWORK_SERVICE);
427             iface = tnm.createTapInterface();
428         } finally {
429             // Drop the identity in order to regain the network stack permissions, which the shell
430             // does not have.
431             inst.getUiAutomation().dropShellPermissionIdentity();
432         }
433         mIfaceName = iface.getInterfaceName();
434         mClientMac = InterfaceParams.getByName(mIfaceName).macAddr.toByteArray();
435         mPacketReaderThread = new HandlerThread(IpClientIntegrationTest.class.getSimpleName());
436         mPacketReaderThread.start();
437         mHandler = mPacketReaderThread.getThreadHandler();
438 
439         // Detach the FileDescriptor from the ParcelFileDescriptor.
440         // Otherwise, the garbage collector might call the ParcelFileDescriptor's finalizer, which
441         // closes the FileDescriptor and destroys our tap interface. An alternative would be to
442         // make the ParcelFileDescriptor or the TestNetworkInterface a class member so they never
443         // go out of scope.
444         mTapFd = new FileDescriptor();
445         mTapFd.setInt$(iface.getFileDescriptor().detachFd());
446         mPacketReader = new TapPacketReader(mHandler, mTapFd, DATA_BUFFER_LEN);
447         mHandler.post(() -> mPacketReader.start());
448     }
449 
450     private void setUpIpClient() throws Exception {
451         final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
452         final IBinder netdIBinder =
453                 (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE);
454         mNetd = spy(INetd.Stub.asInterface(netdIBinder));
455         when(mContext.getSystemService(eq(Context.NETD_SERVICE))).thenReturn(netdIBinder);
456         assertNotNull(mNetd);
457 
458         mNetworkObserverRegistry = new NetworkObserverRegistry();
459         mNetworkObserverRegistry.register(mNetd);
460         mIpc = new IpClient(mContext, mIfaceName, mCb, mNetworkObserverRegistry,
461                 mNetworkStackServiceManager, mDependencies);
462         // Wait for IpClient to enter its initial state. Otherwise, additional setup steps or tests
463         // that mock IpClient's dependencies might interact with those mocks while IpClient is
464         // starting. This would cause UnfinishedStubbingExceptions as mocks cannot be interacted
465         // with while they are being stubbed.
466         HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
467 
468         // Tell the IpMemoryStore immediately to answer any question about network attributes with a
469         // null response. Otherwise, the DHCP client will wait for two seconds before starting,
470         // while its query to the IpMemoryStore times out.
471         // This does not affect any test that makes the mock memory store return results, because
472         // unlike when(), it is documented that doAnswer() can be called more than once, to change
473         // the behaviour of a mock in the middle of a test.
474         doAnswer(invocation -> {
475             final String l2Key = invocation.getArgument(0);
476             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
477                     .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null);
478             return null;
479         }).when(mIpMemoryStore).retrieveNetworkAttributes(any(), any());
480 
481         disableIpv6ProvisioningDelays();
482     }
483 
484     private <T> T verifyWithTimeout(InOrder inOrder, T t) {
485         if (inOrder != null) {
486             return inOrder.verify(t, timeout(TEST_TIMEOUT_MS));
487         } else {
488             return verify(t, timeout(TEST_TIMEOUT_MS));
489         }
490     }
491 
492     private void expectAlarmCancelled(InOrder inOrder, OnAlarmListener listener) {
493         inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).cancel(eq(listener));
494     }
495 
496     private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, int afterSeconds) {
497         // Allow +/- 3 seconds to prevent flaky tests.
498         final long when = SystemClock.elapsedRealtime() + afterSeconds * 1000;
499         final long min = when - 3 * 1000;
500         final long max = when + 3 * 1000;
501         ArgumentCaptor<OnAlarmListener> captor = ArgumentCaptor.forClass(OnAlarmListener.class);
502         verifyWithTimeout(inOrder, mAlarm).setExact(
503                 anyInt(), longThat(x -> x >= min && x <= max),
504                 contains(tagMatch), captor.capture(), eq(mIpc.getHandler()));
505         return captor.getValue();
506     }
507 
508     private boolean packetContainsExpectedField(final byte[] packet, final int offset,
509             final byte[] expected) {
510         if (packet.length < offset + expected.length) return false;
511         for (int i = 0; i < expected.length; ++i) {
512             if (packet[offset + i] != expected[i]) return false;
513         }
514         return true;
515     }
516 
517     private boolean isDhcpPacket(final byte[] packet) {
518         final ByteBuffer buffer = ByteBuffer.wrap(packet);
519 
520         // check the packet length
521         if (packet.length < DHCP_HEADER_OFFSET) return false;
522 
523         // check the source port and dest port in UDP header
524         buffer.position(UDP_SRC_PORT_OFFSET);
525         final short udpSrcPort = buffer.getShort();
526         final short udpDstPort = buffer.getShort();
527         if (udpSrcPort != DHCP_CLIENT || udpDstPort != DHCP_SERVER) return false;
528 
529         // check DHCP message type
530         buffer.position(DHCP_MESSAGE_OP_CODE_OFFSET);
531         final byte dhcpOpCode = buffer.get();
532         if (dhcpOpCode != DHCP_BOOTREQUEST) return false;
533 
534         // check DHCP magic cookie
535         buffer.position(DHCP_OPTION_MAGIC_COOKIE_OFFSET);
536         final int dhcpMagicCookie = buffer.getInt();
537         if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) return false;
538 
539         return true;
540     }
541 
542     private ArpPacket parseArpPacketOrNull(final byte[] packet) {
543         try {
544             return ArpPacket.parseArpPacket(packet, packet.length);
545         } catch (ArpPacket.ParseException e) {
546             return null;
547         }
548     }
549 
550     private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet,
551             final Integer leaseTimeSec, final short mtu, final String captivePortalUrl) {
552         return DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
553                 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
554                 CLIENT_ADDR /* yourIp */, packet.getClientMac(), leaseTimeSec,
555                 NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */,
556                 Collections.singletonList(SERVER_ADDR) /* gateways */,
557                 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
558                 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
559                 false /* metered */, mtu, captivePortalUrl);
560     }
561 
562     private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet,
563             final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu,
564             final boolean rapidCommit, final String captivePortalApiUrl) {
565         return DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
566                 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
567                 clientAddress /* yourIp */, CLIENT_ADDR /* requestIp */, packet.getClientMac(),
568                 leaseTimeSec, NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */,
569                 Collections.singletonList(SERVER_ADDR) /* gateways */,
570                 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
571                 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
572                 false /* metered */, mtu, rapidCommit, captivePortalApiUrl);
573     }
574 
575     private static ByteBuffer buildDhcpNakPacket(final DhcpPacket packet) {
576         return DhcpPacket.buildNakPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
577             SERVER_ADDR /* serverIp */, INADDR_ANY /* relayIp */, packet.getClientMac(),
578             false /* broadcast */, "duplicated request IP address");
579     }
580 
581     private void sendArpReply(final byte[] clientMac) throws IOException {
582         final ByteBuffer packet = ArpPacket.buildArpPacket(clientMac /* dst */,
583                 SERVER_MAC /* src */, INADDR_ANY.getAddress() /* target IP */,
584                 clientMac /* target HW address */, CLIENT_ADDR.getAddress() /* sender IP */,
585                 (short) ARP_REPLY);
586         mPacketReader.sendResponse(packet);
587     }
588 
589     private void sendArpProbe() throws IOException {
590         final ByteBuffer packet = ArpPacket.buildArpPacket(DhcpPacket.ETHER_BROADCAST /* dst */,
591                 SERVER_MAC /* src */, CLIENT_ADDR.getAddress() /* target IP */,
592                 new byte[ETHER_ADDR_LEN] /* target HW address */,
593                 INADDR_ANY.getAddress() /* sender IP */, (short) ARP_REQUEST);
594         mPacketReader.sendResponse(packet);
595     }
596 
597     private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
598             final boolean shouldReplyRapidCommitAck, final boolean isPreconnectionEnabled,
599             final boolean isDhcpIpConflictDetectEnabled,
600             final boolean isHostnameConfigurationEnabled, final String hostname,
601             final String displayName, final ScanResultInfo scanResultInfo)
602             throws RemoteException {
603         ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
604                 .withoutIpReachabilityMonitor()
605                 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
606                         MacAddress.fromString(TEST_DEFAULT_BSSID)))
607                 .withoutIPv6();
608         if (isPreconnectionEnabled) prov.withPreconnection();
609         if (displayName != null) prov.withDisplayName(displayName);
610         if (scanResultInfo != null) prov.withScanResultInfo(scanResultInfo);
611 
612         mDependencies.setDhcpLeaseCacheEnabled(isDhcpLeaseCacheEnabled);
613         mDependencies.setDhcpRapidCommitEnabled(shouldReplyRapidCommitAck);
614         mDependencies.setDhcpIpConflictDetectEnabled(isDhcpIpConflictDetectEnabled);
615         mDependencies.setHostnameConfiguration(isHostnameConfigurationEnabled, hostname);
616         mIpc.startProvisioning(prov.build());
617         if (!isPreconnectionEnabled) {
618             verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
619         }
620         verify(mCb, never()).onProvisioningFailure(any());
621     }
622 
623     private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
624             final boolean isDhcpRapidCommitEnabled, final boolean isPreconnectionEnabled,
625             final boolean isDhcpIpConflictDetectEnabled)
626             throws RemoteException {
627         startIpClientProvisioning(isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled,
628                 isPreconnectionEnabled, isDhcpIpConflictDetectEnabled,
629                 false /* isHostnameConfigurationEnabled */, null /* hostname */,
630                 null /* displayName */, null /* ScanResultInfo */);
631     }
632 
633     private void assertIpMemoryStoreNetworkAttributes(final Integer leaseTimeSec,
634             final long startTime, final int mtu) {
635         final ArgumentCaptor<NetworkAttributes> networkAttributes =
636                 ArgumentCaptor.forClass(NetworkAttributes.class);
637 
638         verify(mIpMemoryStore, timeout(TEST_TIMEOUT_MS))
639             .storeNetworkAttributes(eq(TEST_L2KEY), networkAttributes.capture(), any());
640         final NetworkAttributes naValueCaptured = networkAttributes.getValue();
641         assertEquals(CLIENT_ADDR, naValueCaptured.assignedV4Address);
642         if (leaseTimeSec == null || leaseTimeSec.intValue() == DhcpPacket.INFINITE_LEASE) {
643             assertEquals(Long.MAX_VALUE, naValueCaptured.assignedV4AddressExpiry.longValue());
644         } else {
645             // check the lease expiry's scope
646             final long upperBound = startTime + 7_200_000; // start timestamp + 2h
647             final long lowerBound = startTime + 3_600_000; // start timestamp + 1h
648             final long expiry = naValueCaptured.assignedV4AddressExpiry;
649             assertTrue(upperBound > expiry);
650             assertTrue(lowerBound < expiry);
651         }
652         assertEquals(Collections.singletonList(SERVER_ADDR), naValueCaptured.dnsAddresses);
653         assertEquals(new Integer(mtu), naValueCaptured.mtu);
654     }
655 
656     private void assertIpMemoryNeverStoreNetworkAttributes() {
657         verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any());
658     }
659 
660     private void assertHostname(final boolean isHostnameConfigurationEnabled,
661             final String hostname, final String hostnameAfterTransliteration,
662             final List<DhcpPacket> packetList) throws Exception {
663         for (DhcpPacket packet : packetList) {
664             if (!isHostnameConfigurationEnabled || hostname == null) {
665                 assertNoHostname(packet.getHostname());
666             } else {
667                 assertEquals(packet.getHostname(), hostnameAfterTransliteration);
668             }
669         }
670     }
671 
672     private void assertNoHostname(String hostname) {
673         if (ShimUtils.isAtLeastR()) {
674             assertNull(hostname);
675         } else {
676             // Until Q, if no hostname is set, the device falls back to the hostname set via
677             // system property, to avoid breaking Q devices already launched with that setup.
678             assertEquals(SystemProperties.get("net.hostname"), hostname);
679         }
680     }
681 
682     // Helper method to complete DHCP 2-way or 4-way handshake
683     private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
684             final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
685             final boolean shouldReplyRapidCommitAck, final int mtu,
686             final boolean isDhcpIpConflictDetectEnabled,
687             final boolean isHostnameConfigurationEnabled, final String hostname,
688             final String captivePortalApiUrl, final String displayName,
689             final ScanResultInfo scanResultInfo) throws Exception {
690         startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
691                 false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled,
692                 isHostnameConfigurationEnabled, hostname, displayName, scanResultInfo);
693         return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu,
694                 captivePortalApiUrl);
695     }
696 
697     private List<DhcpPacket> handleDhcpPackets(final boolean isSuccessLease,
698             final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu,
699             final String captivePortalApiUrl) throws Exception {
700         final List<DhcpPacket> packetList = new ArrayList<>();
701         DhcpPacket packet;
702         while ((packet = getNextDhcpPacket()) != null) {
703             packetList.add(packet);
704             if (packet instanceof DhcpDiscoverPacket) {
705                 if (shouldReplyRapidCommitAck) {
706                     mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec,
707                               (short) mtu, true /* rapidCommit */, captivePortalApiUrl));
708                 } else {
709                     mPacketReader.sendResponse(buildDhcpOfferPacket(packet, leaseTimeSec,
710                             (short) mtu, captivePortalApiUrl));
711                 }
712             } else if (packet instanceof DhcpRequestPacket) {
713                 final ByteBuffer byteBuffer = isSuccessLease
714                         ? buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec, (short) mtu,
715                                 false /* rapidCommit */, captivePortalApiUrl)
716                         : buildDhcpNakPacket(packet);
717                 mPacketReader.sendResponse(byteBuffer);
718             } else {
719                 fail("invalid DHCP packet");
720             }
721 
722             // wait for reply to DHCPOFFER packet if disabling rapid commit option
723             if (shouldReplyRapidCommitAck || !(packet instanceof DhcpDiscoverPacket)) {
724                 return packetList;
725             }
726         }
727         fail("No DHCPREQUEST received on interface");
728         return packetList;
729     }
730 
731     private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease,
732             final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
733             final boolean isDhcpRapidCommitEnabled, final int mtu,
734             final boolean isDhcpIpConflictDetectEnabled) throws Exception {
735         return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled,
736                 isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled,
737                 false /* isHostnameConfigurationEnabled */, null /* hostname */,
738                 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */);
739     }
740 
741     private List<DhcpPacket> performDhcpHandshake() throws Exception {
742         return performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
743                 false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
744                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
745     }
746 
747     private DhcpPacket getNextDhcpPacket() throws ParseException {
748         byte[] packet;
749         while ((packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS)) != null) {
750             if (!isDhcpPacket(packet)) continue;
751             return DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L2);
752         }
753         fail("No expected DHCP packet received on interface within timeout");
754         return null;
755     }
756 
757     private DhcpPacket getReplyFromDhcpLease(final NetworkAttributes na, boolean timeout)
758             throws Exception {
759         doAnswer(invocation -> {
760             if (timeout) return null;
761             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
762                     .onNetworkAttributesRetrieved(new Status(SUCCESS), TEST_L2KEY, na);
763             return null;
764         }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any());
765         startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */,
766                 false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
767                 false /* isDhcpIpConflictDetectEnabled */);
768         return getNextDhcpPacket();
769     }
770 
771     private void removeTapInterface(final FileDescriptor fd) {
772         try {
773             Os.close(fd);
774         } catch (ErrnoException e) {
775             fail("Fail to close file descriptor: " + e);
776         }
777     }
778 
779     private void verifyAfterIpClientShutdown() throws RemoteException {
780         final LinkProperties emptyLp = new LinkProperties();
781         emptyLp.setInterfaceName(mIfaceName);
782         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(emptyLp);
783     }
784 
785     // Verify IPv4-only provisioning success. No need to verify IPv4 provisioning when below cases
786     // happen:
787     // 1. if there's a failure lease, onProvisioningSuccess() won't be called;
788     // 2. if duplicated IPv4 address detection is enabled, verify TIMEOUT will affect ARP packets
789     //    capture running in other test cases.
790     // 3. if IPv6 is enabled, e.g. withoutIPv6() isn't called when starting provisioning.
791     private void verifyIPv4OnlyProvisioningSuccess(final Collection<InetAddress> addresses)
792             throws Exception {
793         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
794         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
795         LinkProperties lp = captor.getValue();
796         assertNotNull(lp);
797         assertNotEquals(0, lp.getDnsServers().size());
798         assertEquals(addresses.size(), lp.getAddresses().size());
799         assertTrue(lp.getAddresses().containsAll(addresses));
800     }
801 
802     private void doRestoreInitialMtuTest(final boolean shouldChangeMtu,
803             final boolean shouldRemoveTapInterface) throws Exception {
804         final long currentTime = System.currentTimeMillis();
805         int mtu = TEST_DEFAULT_MTU;
806 
807         if (shouldChangeMtu) mtu = TEST_MIN_MTU;
808         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
809                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
810                 mtu, false /* isDhcpIpConflictDetectEnabled */);
811         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
812         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, mtu);
813 
814         if (shouldChangeMtu) {
815             // Pretend that ConnectivityService set the MTU.
816             mNetd.interfaceSetMtu(mIfaceName, mtu);
817             assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), mtu);
818         }
819 
820         // Sometimes, IpClient receives an update with an empty LinkProperties during startup,
821         // when the link-local address is deleted after interface bringup. Reset expectations
822         // here to ensure that verifyAfterIpClientShutdown does not fail because it sees two
823         // empty LinkProperties changes instead of one.
824         reset(mCb);
825 
826         if (shouldRemoveTapInterface) removeTapInterface(mTapFd);
827         try {
828             mIpc.shutdown();
829             awaitIpClientShutdown();
830             if (shouldRemoveTapInterface) {
831                 verify(mNetd, never()).interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
832             } else {
833                 // Verify that MTU indeed has been restored or not.
834                 verify(mNetd, times(shouldChangeMtu ? 1 : 0))
835                         .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
836             }
837             verifyAfterIpClientShutdown();
838         } catch (Exception e) {
839             fail("Exception should not have been thrown after shutdown: " + e);
840         }
841     }
842 
843     private DhcpPacket assertDiscoverPacketOnPreconnectionStart() throws Exception {
844         final ArgumentCaptor<List<Layer2PacketParcelable>> l2PacketList =
845                 ArgumentCaptor.forClass(List.class);
846 
847         verify(mCb, timeout(TEST_TIMEOUT_MS)).onPreconnectionStart(l2PacketList.capture());
848         final byte[] payload = l2PacketList.getValue().get(0).payload;
849         DhcpPacket packet = DhcpPacket.decodeFullPacket(payload, payload.length, ENCAP_L2);
850         assertTrue(packet instanceof DhcpDiscoverPacket);
851         assertArrayEquals(INADDR_BROADCAST.getAddress(),
852                 Arrays.copyOfRange(payload, IPV4_DST_ADDR_OFFSET, IPV4_DST_ADDR_OFFSET + 4));
853         return packet;
854     }
855 
856     private void doIpClientProvisioningWithPreconnectionTest(
857             final boolean shouldReplyRapidCommitAck, final boolean shouldAbortPreconnection,
858             final boolean shouldFirePreconnectionTimeout,
859             final boolean timeoutBeforePreconnectionComplete) throws Exception {
860         final long currentTime = System.currentTimeMillis();
861         final ArgumentCaptor<InterfaceConfigurationParcel> ifConfig =
862                 ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
863 
864         startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */,
865                 shouldReplyRapidCommitAck, true /* isDhcpPreConnectionEnabled */,
866                 false /* isDhcpIpConflictDetectEnabled */);
867         DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart();
868         final int preconnDiscoverTransId = packet.getTransactionId();
869 
870         if (shouldAbortPreconnection) {
871             if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) {
872                 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
873             }
874 
875             mIpc.notifyPreconnectionComplete(false /* abort */);
876             HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
877 
878             if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) {
879                 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
880             }
881 
882             // Either way should get DhcpClient go back to INIT state, and broadcast
883             // DISCOVER with new transaction ID.
884             packet = getNextDhcpPacket();
885             assertTrue(packet instanceof DhcpDiscoverPacket);
886             assertTrue(packet.getTransactionId() != preconnDiscoverTransId);
887         } else if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) {
888             // If timeout fires before success preconnection, DhcpClient will go back to INIT state,
889             // and broadcast DISCOVER with new transaction ID.
890             mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
891             packet = getNextDhcpPacket();
892             assertTrue(packet instanceof DhcpDiscoverPacket);
893             assertTrue(packet.getTransactionId() != preconnDiscoverTransId);
894             // any old response would be ignored due to mismatched transaction ID.
895         }
896 
897         final short mtu = (short) TEST_DEFAULT_MTU;
898         if (!shouldReplyRapidCommitAck) {
899             mPacketReader.sendResponse(buildDhcpOfferPacket(packet, TEST_LEASE_DURATION_S, mtu,
900                     null /* captivePortalUrl */));
901             packet = getNextDhcpPacket();
902             assertTrue(packet instanceof DhcpRequestPacket);
903         }
904         mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, TEST_LEASE_DURATION_S,
905                 mtu, shouldReplyRapidCommitAck, null /* captivePortalUrl */));
906 
907         if (!shouldAbortPreconnection) {
908             mIpc.notifyPreconnectionComplete(true /* success */);
909             HandlerUtilsKt.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
910 
911             // If timeout fires after successful preconnection, right now DhcpClient will have
912             // already entered BOUND state, the delayed CMD_TIMEOUT command would be ignored. So
913             // this case should be very rare, because the timeout alarm is cancelled when state
914             // machine exits from Preconnecting state.
915             if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) {
916                 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT);
917             }
918         }
919         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
920 
921         final LinkAddress ipAddress = new LinkAddress(CLIENT_ADDR, PREFIX_LENGTH);
922         verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetCfg(ifConfig.capture());
923         assertEquals(ifConfig.getValue().ifName, mIfaceName);
924         assertEquals(ifConfig.getValue().ipv4Addr, ipAddress.getAddress().getHostAddress());
925         assertEquals(ifConfig.getValue().prefixLength, PREFIX_LENGTH);
926         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
927     }
928 
929     private ArpPacket getNextArpPacket(final int timeout) throws Exception {
930         byte[] packet;
931         while ((packet = mPacketReader.popPacket(timeout)) != null) {
932             final ArpPacket arpPacket = parseArpPacketOrNull(packet);
933             if (arpPacket != null) return arpPacket;
934         }
935         return null;
936     }
937 
938     private ArpPacket getNextArpPacket() throws Exception {
939         final ArpPacket packet = getNextArpPacket(PACKET_TIMEOUT_MS);
940         assertNotNull("No expected ARP packet received on interface within timeout", packet);
941         return packet;
942     }
943 
944     private void assertArpPacket(final ArpPacket packet) {
945         assertEquals(packet.opCode, ARP_REQUEST);
946         assertEquals(packet.targetIp, CLIENT_ADDR);
947         assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac));
948     }
949 
950     private void assertArpProbe(final ArpPacket packet) {
951         assertArpPacket(packet);
952         assertEquals(packet.senderIp, INADDR_ANY);
953     }
954 
955     private void assertArpAnnounce(final ArpPacket packet) {
956         assertArpPacket(packet);
957         assertEquals(packet.senderIp, CLIENT_ADDR);
958     }
959 
960     private void doIpAddressConflictDetectionTest(final boolean causeIpAddressConflict,
961             final boolean shouldReplyRapidCommitAck, final boolean isDhcpIpConflictDetectEnabled,
962             final boolean shouldResponseArpReply) throws Exception {
963         final long currentTime = System.currentTimeMillis();
964 
965         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
966                 true /* isDhcpLeaseCacheEnabled */, shouldReplyRapidCommitAck,
967                 TEST_DEFAULT_MTU, isDhcpIpConflictDetectEnabled);
968 
969         // If we receive an ARP packet here, it's guaranteed to be from IP conflict detection,
970         // because at this time the test interface does not have an IP address and therefore
971         // won't send ARP for anything.
972         if (causeIpAddressConflict) {
973             final ArpPacket arpProbe = getNextArpPacket();
974             assertArpProbe(arpProbe);
975 
976             if (shouldResponseArpReply) {
977                 sendArpReply(mClientMac);
978             } else {
979                 sendArpProbe();
980             }
981             final DhcpPacket packet = getNextDhcpPacket();
982             assertTrue(packet instanceof DhcpDeclinePacket);
983             assertEquals(packet.mServerIdentifier, SERVER_ADDR);
984             assertEquals(packet.mRequestedIp, CLIENT_ADDR);
985 
986             verify(mCb, never()).onProvisioningFailure(any());
987             assertIpMemoryNeverStoreNetworkAttributes();
988         } else if (isDhcpIpConflictDetectEnabled) {
989             int arpPacketCount = 0;
990             final List<ArpPacket> packetList = new ArrayList<ArpPacket>();
991             // Total sent ARP packets should be 5 (3 ARP Probes + 2 ARP Announcements)
992             ArpPacket packet;
993             while ((packet = getNextArpPacket(TEST_TIMEOUT_MS)) != null) {
994                 packetList.add(packet);
995             }
996             assertEquals(5, packetList.size());
997             assertArpProbe(packetList.get(0));
998             assertArpAnnounce(packetList.get(3));
999         } else {
1000             verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1001             assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime,
1002                     TEST_DEFAULT_MTU);
1003         }
1004     }
1005 
1006     @Test
1007     public void testInterfaceParams() throws Exception {
1008         InterfaceParams params = InterfaceParams.getByName(mIfaceName);
1009         assertNotNull(params);
1010         assertEquals(mIfaceName, params.name);
1011         assertTrue(params.index > 0);
1012         assertNotNull(params.macAddr);
1013         assertTrue(params.hasMacAddress);
1014 
1015         // Sanity check.
1016         params = InterfaceParams.getByName("lo");
1017         assertNotNull(params);
1018         assertEquals("lo", params.name);
1019         assertTrue(params.index > 0);
1020         assertNotNull(params.macAddr);
1021         assertFalse(params.hasMacAddress);
1022     }
1023 
1024     @Test
1025     public void testDhcpInit() throws Exception {
1026         startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
1027                 false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
1028                 false /* isDhcpIpConflictDetectEnabled */);
1029         final DhcpPacket packet = getNextDhcpPacket();
1030         assertTrue(packet instanceof DhcpDiscoverPacket);
1031     }
1032 
1033     @Test
1034     public void testHandleSuccessDhcpLease() throws Exception {
1035         final long currentTime = System.currentTimeMillis();
1036         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1037                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1038                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1039         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1040         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1041     }
1042 
1043     @Test
1044     public void testHandleFailureDhcpLease() throws Exception {
1045         performDhcpHandshake(false /* isSuccessLease */, TEST_LEASE_DURATION_S,
1046                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1047                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1048 
1049         verify(mCb, never()).onProvisioningSuccess(any());
1050         assertIpMemoryNeverStoreNetworkAttributes();
1051     }
1052 
1053     @Test
1054     public void testHandleInfiniteLease() throws Exception {
1055         final long currentTime = System.currentTimeMillis();
1056         performDhcpHandshake(true /* isSuccessLease */, INFINITE_LEASE,
1057                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1058                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1059         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1060         assertIpMemoryStoreNetworkAttributes(INFINITE_LEASE, currentTime, TEST_DEFAULT_MTU);
1061     }
1062 
1063     @Test
1064     public void testHandleNoLease() throws Exception {
1065         final long currentTime = System.currentTimeMillis();
1066         performDhcpHandshake(true /* isSuccessLease */, null /* no lease time */,
1067                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1068                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1069         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1070         assertIpMemoryStoreNetworkAttributes(null, currentTime, TEST_DEFAULT_MTU);
1071     }
1072 
1073     @Test @IgnoreAfter(Build.VERSION_CODES.Q) // INIT-REBOOT is enabled on R.
1074     public void testHandleDisableInitRebootState() throws Exception {
1075         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1076                 false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1077                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1078         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1079         assertIpMemoryNeverStoreNetworkAttributes();
1080     }
1081 
1082     @Test
1083     public void testHandleRapidCommitOption() throws Exception {
1084         final long currentTime = System.currentTimeMillis();
1085         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1086                 true /* isDhcpLeaseCacheEnabled */, true /* shouldReplyRapidCommitAck */,
1087                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1088         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1089         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1090     }
1091 
1092     @Test
1093     public void testDhcpClientStartWithCachedInfiniteLease() throws Exception {
1094         final DhcpPacket packet = getReplyFromDhcpLease(
1095                 new NetworkAttributes.Builder()
1096                     .setAssignedV4Address(CLIENT_ADDR)
1097                     .setAssignedV4AddressExpiry(Long.MAX_VALUE) // lease is always valid
1098                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1099                     .setCluster(TEST_CLUSTER)
1100                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1101                     .build(), false /* timeout */);
1102         assertTrue(packet instanceof DhcpRequestPacket);
1103     }
1104 
1105     @Test
1106     public void testDhcpClientStartWithCachedExpiredLease() throws Exception {
1107         final DhcpPacket packet = getReplyFromDhcpLease(
1108                  new NetworkAttributes.Builder()
1109                     .setAssignedV4Address(CLIENT_ADDR)
1110                     .setAssignedV4AddressExpiry(EXPIRED_LEASE)
1111                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1112                     .setCluster(TEST_CLUSTER)
1113                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1114                     .build(), false /* timeout */);
1115         assertTrue(packet instanceof DhcpDiscoverPacket);
1116     }
1117 
1118     @Test
1119     public void testDhcpClientStartWithNullRetrieveNetworkAttributes() throws Exception {
1120         final DhcpPacket packet = getReplyFromDhcpLease(null /* na */, false /* timeout */);
1121         assertTrue(packet instanceof DhcpDiscoverPacket);
1122     }
1123 
1124     @Test
1125     public void testDhcpClientStartWithTimeoutRetrieveNetworkAttributes() throws Exception {
1126         final DhcpPacket packet = getReplyFromDhcpLease(
1127                 new NetworkAttributes.Builder()
1128                     .setAssignedV4Address(CLIENT_ADDR)
1129                     .setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000)
1130                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1131                     .setCluster(TEST_CLUSTER)
1132                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1133                     .build(), true /* timeout */);
1134         assertTrue(packet instanceof DhcpDiscoverPacket);
1135     }
1136 
1137     @Test
1138     public void testDhcpClientStartWithCachedLeaseWithoutIPAddress() throws Exception {
1139         final DhcpPacket packet = getReplyFromDhcpLease(
1140                 new NetworkAttributes.Builder()
1141                     .setMtu(new Integer(TEST_DEFAULT_MTU))
1142                     .setCluster(TEST_CLUSTER)
1143                     .setDnsAddresses(Collections.singletonList(SERVER_ADDR))
1144                     .build(), false /* timeout */);
1145         assertTrue(packet instanceof DhcpDiscoverPacket);
1146     }
1147 
1148     @Test
1149     public void testDhcpClientRapidCommitEnabled() throws Exception {
1150         startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */,
1151                 true /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */,
1152                 false /* isDhcpIpConflictDetectEnabled */);
1153         final DhcpPacket packet = getNextDhcpPacket();
1154         assertTrue(packet instanceof DhcpDiscoverPacket);
1155     }
1156 
1157     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
1158     public void testDhcpServerInLinkProperties() throws Exception {
1159         assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q);
1160 
1161         performDhcpHandshake();
1162         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1163         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
1164         assertEquals(SERVER_ADDR, captor.getValue().getDhcpServerAddress());
1165     }
1166 
1167     @Test
1168     public void testRestoreInitialInterfaceMtu() throws Exception {
1169         doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */);
1170     }
1171 
1172     @Test
1173     public void testRestoreInitialInterfaceMtu_WithoutMtuChange() throws Exception {
1174         doRestoreInitialMtuTest(false /* shouldChangeMtu */, false /* shouldRemoveTapInterface */);
1175     }
1176 
1177     @Test
1178     public void testRestoreInitialInterfaceMtu_WithException() throws Exception {
1179         doThrow(new RemoteException("NetdNativeService::interfaceSetMtu")).when(mNetd)
1180                 .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU);
1181 
1182         doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */);
1183         assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU);
1184     }
1185 
1186     @Test
1187     public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStopping() throws Exception {
1188         doRestoreInitialMtuTest(true /* shouldChangeMtu */, true /* shouldRemoveTapInterface */);
1189     }
1190 
1191     @Test
1192     public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStartingProvisioning()
1193             throws Exception {
1194         removeTapInterface(mTapFd);
1195         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1196                 .withoutIpReachabilityMonitor()
1197                 .withoutIPv6()
1198                 .build();
1199 
1200         mIpc.startProvisioning(config);
1201         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
1202         verify(mCb, never()).setNeighborDiscoveryOffload(true);
1203     }
1204 
1205     @Test
1206     public void testRestoreInitialInterfaceMtu_stopIpClientAndRestart() throws Exception {
1207         long currentTime = System.currentTimeMillis();
1208 
1209         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1210                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1211                 TEST_MIN_MTU, false /* isDhcpIpConflictDetectEnabled */);
1212         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1213         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_MIN_MTU);
1214 
1215         // Pretend that ConnectivityService set the MTU.
1216         mNetd.interfaceSetMtu(mIfaceName, TEST_MIN_MTU);
1217         assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU);
1218 
1219         reset(mCb);
1220         reset(mIpMemoryStore);
1221 
1222         // Stop IpClient and then restart provisioning immediately.
1223         mIpc.stop();
1224         currentTime = System.currentTimeMillis();
1225         // Intend to set mtu option to 0, then verify that won't influence interface mtu restore.
1226         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1227                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1228                 0 /* mtu */, false /* isDhcpIpConflictDetectEnabled */);
1229         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1230         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 0 /* mtu */);
1231         assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_DEFAULT_MTU);
1232     }
1233 
1234     private boolean isRouterSolicitation(final byte[] packetBytes) {
1235         ByteBuffer packet = ByteBuffer.wrap(packetBytes);
1236         return packet.getShort(ETHER_TYPE_OFFSET) == (short) ETH_P_IPV6
1237                 && packet.get(ETHER_HEADER_LEN + IPV6_PROTOCOL_OFFSET) == (byte) IPPROTO_ICMPV6
1238                 && packet.get(ETHER_HEADER_LEN + IPV6_HEADER_LEN)
1239                         == (byte) ICMPV6_ROUTER_SOLICITATION;
1240     }
1241 
1242     private void waitForRouterSolicitation() throws ParseException {
1243         byte[] packet;
1244         while ((packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS)) != null) {
1245             if (isRouterSolicitation(packet)) return;
1246         }
1247         fail("No router solicitation received on interface within timeout");
1248     }
1249 
1250     private void sendRouterAdvertisement(boolean waitForRs, short lifetime) throws Exception {
1251         final String dnsServer = "2001:4860:4860::64";
1252         final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64");
1253         ByteBuffer rdnss = buildRdnssOption(3600, dnsServer);
1254         ByteBuffer ra = buildRaPacket(lifetime, pio, rdnss);
1255 
1256         if (waitForRs) {
1257             waitForRouterSolicitation();
1258         }
1259 
1260         mPacketReader.sendResponse(ra);
1261     }
1262 
1263     private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception {
1264         sendRouterAdvertisement(waitForRs, (short) 1800);
1265     }
1266 
1267     private void sendRouterAdvertisementWithZeroLifetime() throws Exception {
1268         sendRouterAdvertisement(false /* waitForRs */, (short) 0);
1269     }
1270 
1271     // TODO: move this and the following method to a common location and use them in ApfTest.
1272     private static ByteBuffer buildPioOption(int valid, int preferred, String prefixString)
1273             throws Exception {
1274         final int optLen = 4;
1275         IpPrefix prefix = new IpPrefix(prefixString);
1276         ByteBuffer option = ByteBuffer.allocate(optLen * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR);
1277         option.put((byte) ICMPV6_ND_OPTION_PIO);      // Type
1278         option.put((byte) optLen);                    // Length in 8-byte units
1279         option.put((byte) prefix.getPrefixLength());  // Prefix length
1280         option.put((byte) 0b11000000);                // L = 1, A = 1
1281         option.putInt(valid);
1282         option.putInt(preferred);
1283         option.putInt(0);                             // Reserved
1284         option.put(prefix.getRawAddress());
1285         option.flip();
1286         return option;
1287     }
1288 
1289     private static ByteBuffer buildRdnssOption(int lifetime, String... servers) throws Exception {
1290         final int optLen = 1 + 2 * servers.length;
1291         ByteBuffer option = ByteBuffer.allocate(optLen * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR);
1292         option.put((byte) ICMPV6_ND_OPTION_RDNSS);  // Type
1293         option.put((byte) optLen);                  // Length in 8-byte units
1294         option.putShort((short) 0);                 // Reserved
1295         option.putInt(lifetime);                    // Lifetime
1296         for (String server : servers) {
1297             option.put(InetAddress.getByName(server).getAddress());
1298         }
1299         option.flip();
1300         return option;
1301     }
1302 
1303     // HACK: these functions are here because IpUtils#transportChecksum is private. Even if we made
1304     // that public, it won't be available on Q devices, and this test needs to run on Q devices.
1305     // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
1306     private static int checksumFold(int sum) {
1307         while (sum > 0xffff) {
1308             sum = (sum >> 16) + (sum & 0xffff);
1309         }
1310         return sum;
1311     }
1312 
1313     private static short checksumAdjust(short checksum, short oldWord, short newWord) {
1314         checksum = (short) ~checksum;
1315         int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord));
1316         return (short) ~tempSum;
1317     }
1318 
1319     public static int uint16(short s) {
1320         return s & 0xffff;
1321     }
1322 
1323     private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset,
1324             int transportLen) {
1325         // The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses
1326         // 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental
1327         // checksum adjustment  for the change in the next header byte.
1328         short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen);
1329         return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6);
1330     }
1331 
1332     private static ByteBuffer buildRaPacket(short lifetime, ByteBuffer... options)
1333             throws Exception {
1334         final MacAddress srcMac = MacAddress.fromString("33:33:00:00:00:01");
1335         final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06");
1336         final byte[] routerLinkLocal = InetAddresses.parseNumericAddress("fe80::1").getAddress();
1337         final byte[] allNodes = InetAddresses.parseNumericAddress("ff02::1").getAddress();
1338 
1339         final ByteBuffer packet = ByteBuffer.allocate(TEST_DEFAULT_MTU);
1340         int icmpLen = ICMPV6_RA_HEADER_LEN;
1341 
1342         // Ethernet header.
1343         packet.put(srcMac.toByteArray());
1344         packet.put(dstMac.toByteArray());
1345         packet.putShort((short) ETHER_TYPE_IPV6);
1346 
1347         // IPv6 header.
1348         packet.putInt(0x600abcde);                       // Version, traffic class, flowlabel
1349         packet.putShort((short) 0);                      // Length, TBD
1350         packet.put((byte) IPPROTO_ICMPV6);               // Next header
1351         packet.put((byte) 0xff);                         // Hop limit
1352         packet.put(routerLinkLocal);                     // Source address
1353         packet.put(allNodes);                            // Destination address
1354 
1355         // Router advertisement.
1356         packet.put((byte) ICMPV6_ROUTER_ADVERTISEMENT);  // ICMP type
1357         packet.put((byte) 0);                            // ICMP code
1358         packet.putShort((short) 0);                      // Checksum, TBD
1359         packet.put((byte) 0);                            // Hop limit, unspecified
1360         packet.put((byte) 0);                            // M=0, O=0
1361         packet.putShort(lifetime);                       // Router lifetime
1362         packet.putInt(0);                                // Reachable time, unspecified
1363         packet.putInt(100);                              // Retrans time 100ms.
1364 
1365         for (ByteBuffer option : options) {
1366             packet.put(option);
1367             option.clear();  // So we can reuse it in a future packet.
1368             icmpLen += option.capacity();
1369         }
1370 
1371         // Populate length and checksum fields.
1372         final int transportOffset = ETHER_HEADER_LEN + IPV6_HEADER_LEN;
1373         final short checksum = icmpv6Checksum(packet, ETHER_HEADER_LEN, transportOffset, icmpLen);
1374         packet.putShort(ETHER_HEADER_LEN + IPV6_LEN_OFFSET, (short) icmpLen);
1375         packet.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum);
1376 
1377         packet.flip();
1378         return packet;
1379     }
1380 
1381     private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception {
1382         return buildRaPacket((short) 1800, options);
1383     }
1384 
1385     private void disableIpv6ProvisioningDelays() throws Exception {
1386         // Speed up the test by disabling DAD and removing router_solicitation_delay.
1387         // We don't need to restore the default value because the interface is removed in tearDown.
1388         // TODO: speed up further by not waiting for RS but keying off first IPv6 packet.
1389         mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "router_solicitation_delay", "0");
1390         mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits", "0");
1391     }
1392 
1393     private void assertHasAddressThat(String msg, LinkProperties lp,
1394             Predicate<LinkAddress> condition) {
1395         for (LinkAddress addr : lp.getLinkAddresses()) {
1396             if (condition.test(addr)) {
1397                 return;
1398             }
1399         }
1400         fail(msg + " not found in: " + lp);
1401     }
1402 
1403     private boolean hasFlag(LinkAddress addr, int flag) {
1404         return (addr.getFlags() & flag) == flag;
1405     }
1406 
1407     private boolean isPrivacyAddress(LinkAddress addr) {
1408         return addr.isGlobalPreferred() && hasFlag(addr, IFA_F_TEMPORARY);
1409     }
1410 
1411     private boolean isStablePrivacyAddress(LinkAddress addr) {
1412         // TODO: move away from getting address updates from netd and make this work on Q as well.
1413         final int flag = ShimUtils.isAtLeastR() ? IFA_F_STABLE_PRIVACY : 0;
1414         return addr.isGlobalPreferred() && hasFlag(addr, flag);
1415     }
1416 
1417     private LinkProperties doIpv6OnlyProvisioning(InOrder inOrder, ByteBuffer ra) throws Exception {
1418         waitForRouterSolicitation();
1419         mPacketReader.sendResponse(ra);
1420 
1421         // The lambda below needs to write a LinkProperties to a local variable, but lambdas cannot
1422         // write to non-final local variables. So declare a final variable to write to.
1423         final AtomicReference<LinkProperties> lpRef = new AtomicReference<>();
1424 
1425         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1426         verifyWithTimeout(inOrder, mCb).onProvisioningSuccess(captor.capture());
1427         lpRef.set(captor.getValue());
1428 
1429         // Sometimes provisioning completes as soon as the link-local and the stable address appear,
1430         // before the privacy address appears. If so, wait here for the LinkProperties update that
1431         // contains all three address. Otherwise, future calls to verify() might get confused.
1432         if (captor.getValue().getLinkAddresses().size() == 2) {
1433             verifyWithTimeout(inOrder, mCb).onLinkPropertiesChange(argThat(lp -> {
1434                 lpRef.set(lp);
1435                 return lp.getLinkAddresses().size() == 3;
1436             }));
1437         }
1438 
1439         LinkProperties lp = lpRef.get();
1440         assertEquals("Should have 3 IPv6 addresses after provisioning: " + lp,
1441                 3, lp.getLinkAddresses().size());
1442         assertHasAddressThat("link-local address", lp, x -> x.getAddress().isLinkLocalAddress());
1443         assertHasAddressThat("privacy address", lp, this::isPrivacyAddress);
1444         assertHasAddressThat("stable privacy address", lp, this::isStablePrivacyAddress);
1445 
1446         return lp;
1447     }
1448 
1449     @Test
1450     public void testRaRdnss() throws Exception {
1451         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1452                 .withoutIpReachabilityMonitor()
1453                 .withoutIPv4()
1454                 .build();
1455         mIpc.startProvisioning(config);
1456 
1457         InOrder inOrder = inOrder(mCb);
1458         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1459 
1460         final String dnsServer = "2001:4860:4860::64";
1461         final String lowlifeDnsServer = "2001:4860:4860::6464";
1462 
1463         final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64");
1464         ByteBuffer rdnss1 = buildRdnssOption(60, lowlifeDnsServer);
1465         ByteBuffer rdnss2 = buildRdnssOption(600, dnsServer);
1466         ByteBuffer ra = buildRaPacket(pio, rdnss1, rdnss2);
1467 
1468         LinkProperties lp = doIpv6OnlyProvisioning(inOrder, ra);
1469 
1470         // Expect that DNS servers with lifetimes below CONFIG_MIN_RDNSS_LIFETIME are not accepted.
1471         assertNotNull(lp);
1472         assertEquals(1, lp.getDnsServers().size());
1473         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
1474 
1475         // If the RDNSS lifetime is above the minimum, the DNS server is accepted.
1476         rdnss1 = buildRdnssOption(68, lowlifeDnsServer);
1477         ra = buildRaPacket(pio, rdnss1, rdnss2);
1478         mPacketReader.sendResponse(ra);
1479         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(captor.capture());
1480         lp = captor.getValue();
1481         assertNotNull(lp);
1482         assertEquals(2, lp.getDnsServers().size());
1483         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
1484         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(lowlifeDnsServer)));
1485 
1486         // Expect that setting RDNSS lifetime of 0 causes loss of provisioning.
1487         rdnss1 = buildRdnssOption(0, dnsServer);
1488         rdnss2 = buildRdnssOption(0, lowlifeDnsServer);
1489         ra = buildRaPacket(pio, rdnss1, rdnss2);
1490         mPacketReader.sendResponse(ra);
1491 
1492         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
1493         lp = captor.getValue();
1494         assertNotNull(lp);
1495         assertEquals(0, lp.getDnsServers().size());
1496         reset(mCb);
1497     }
1498 
1499     private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception {
1500         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(
1501                 argThat(lp -> Objects.equals(expected, lp.getNat64Prefix())));
1502 
1503     }
1504 
1505     private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception {
1506         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(argThat(
1507                 lp -> !Objects.equals(unchanged, lp.getNat64Prefix())));
1508 
1509     }
1510 
1511     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
1512     public void testPref64Option() throws Exception {
1513         assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q);
1514 
1515         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1516                 .withoutIpReachabilityMonitor()
1517                 .withoutIPv4()
1518                 .build();
1519         mIpc.startProvisioning(config);
1520 
1521         final String dnsServer = "2001:4860:4860::64";
1522         final IpPrefix prefix = new IpPrefix("64:ff9b::/96");
1523         final IpPrefix otherPrefix = new IpPrefix("2001:db8:64::/96");
1524 
1525         final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64");
1526         ByteBuffer rdnss = buildRdnssOption(600, dnsServer);
1527         ByteBuffer pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
1528         ByteBuffer ra = buildRaPacket(pio, rdnss, pref64);
1529 
1530         // The NAT64 prefix might be detected before or after provisioning success.
1531         // Don't test order between these two events.
1532         LinkProperties lp = doIpv6OnlyProvisioning(null /*inOrder*/, ra);
1533         expectAlarmSet(null /*inOrder*/, "PREF64", 600);
1534 
1535         // From now on expect events in order.
1536         InOrder inOrder = inOrder(mCb, mAlarm);
1537         if (lp.getNat64Prefix() != null) {
1538             assertEquals(prefix, lp.getNat64Prefix());
1539         } else {
1540             expectNat64PrefixUpdate(inOrder, prefix);
1541         }
1542 
1543         // Increase the lifetime and expect the prefix not to change.
1544         pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer();
1545         ra = buildRaPacket(pio, rdnss, pref64);
1546         mPacketReader.sendResponse(ra);
1547         OnAlarmListener pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1800);
1548         expectNoNat64PrefixUpdate(inOrder, prefix);
1549         reset(mCb, mAlarm);
1550 
1551         // Reduce the lifetime and expect to reschedule expiry.
1552         pref64 = new StructNdOptPref64(prefix, 1500).toByteBuffer();
1553         ra = buildRaPacket(pio, rdnss, pref64);
1554         mPacketReader.sendResponse(ra);
1555         pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1496);
1556         expectNoNat64PrefixUpdate(inOrder, prefix);
1557         reset(mCb, mAlarm);
1558 
1559         // Withdraw the prefix and expect it to be set to null.
1560         pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer();
1561         ra = buildRaPacket(pio, rdnss, pref64);
1562         mPacketReader.sendResponse(ra);
1563         expectAlarmCancelled(inOrder, pref64Alarm);
1564         expectNat64PrefixUpdate(inOrder, null);
1565         reset(mCb, mAlarm);
1566 
1567         // Re-announce the prefix.
1568         pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
1569         ra = buildRaPacket(pio, rdnss, pref64);
1570         mPacketReader.sendResponse(ra);
1571         expectAlarmSet(inOrder, "PREF64", 600);
1572         expectNat64PrefixUpdate(inOrder, prefix);
1573         reset(mCb, mAlarm);
1574 
1575         // Announce two prefixes. Don't expect any update because if there is already a NAT64
1576         // prefix, any new prefix is ignored.
1577         ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 1200).toByteBuffer();
1578         ra = buildRaPacket(pio, rdnss, pref64, otherPref64);
1579         mPacketReader.sendResponse(ra);
1580         expectAlarmSet(inOrder, "PREF64", 600);
1581         expectNoNat64PrefixUpdate(inOrder, prefix);
1582         reset(mCb, mAlarm);
1583 
1584         // Withdraw the old prefix and continue to announce the new one. Expect a prefix change.
1585         pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer();
1586         ra = buildRaPacket(pio, rdnss, pref64, otherPref64);
1587         mPacketReader.sendResponse(ra);
1588         expectAlarmCancelled(inOrder, pref64Alarm);
1589         // Need a different OnAlarmListener local variable because posting it to the handler in the
1590         // lambda below requires it to be final.
1591         final OnAlarmListener lastAlarm = expectAlarmSet(inOrder, "PREF64", 1200);
1592         expectNat64PrefixUpdate(inOrder, otherPrefix);
1593         reset(mCb, mAlarm);
1594 
1595         // Simulate prefix expiry.
1596         mIpc.getHandler().post(() -> lastAlarm.onAlarm());
1597         expectAlarmCancelled(inOrder, pref64Alarm);
1598         expectNat64PrefixUpdate(inOrder, null);
1599 
1600         // Announce a non-/96 prefix and expect it to be ignored.
1601         IpPrefix invalidPrefix = new IpPrefix("64:ff9b::/64");
1602         pref64 = new StructNdOptPref64(invalidPrefix, 1200).toByteBuffer();
1603         ra = buildRaPacket(pio, rdnss, pref64);
1604         mPacketReader.sendResponse(ra);
1605         expectNoNat64PrefixUpdate(inOrder, invalidPrefix);
1606 
1607         // Re-announce the prefix.
1608         pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
1609         ra = buildRaPacket(pio, rdnss, pref64);
1610         mPacketReader.sendResponse(ra);
1611         final OnAlarmListener clearAlarm = expectAlarmSet(inOrder, "PREF64", 600);
1612         expectNat64PrefixUpdate(inOrder, prefix);
1613         reset(mCb, mAlarm);
1614 
1615         // Check that the alarm is cancelled when IpClient is stopped.
1616         mIpc.stop();
1617         HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1618         expectAlarmCancelled(inOrder, clearAlarm);
1619         expectNat64PrefixUpdate(inOrder, null);
1620 
1621         // Check that even if the alarm was already in the message queue while it was cancelled, it
1622         // is safely ignored.
1623         mIpc.getHandler().post(() -> clearAlarm.onAlarm());
1624         HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1625     }
1626 
1627     private void addIpAddressAndWaitForIt(final String iface) throws Exception {
1628         final CountDownLatch latch = new CountDownLatch(1);
1629 
1630         final String addr1 = "192.0.2.99";
1631         final String addr2 = "192.0.2.3";
1632         final int prefixLength = 26;
1633 
1634         // Add two IPv4 addresses to the specified interface, and proceed when the NetworkObserver
1635         // has seen the second one. This ensures that every other NetworkObserver registered with
1636         // mNetworkObserverRegistry - in particular, IpClient's - has seen the addition of the first
1637         // address.
1638         final LinkAddress trigger = new LinkAddress(addr2 + "/" + prefixLength);
1639         NetworkObserver observer = new NetworkObserver() {
1640             @Override
1641             public void onInterfaceAddressUpdated(LinkAddress address, String ifName) {
1642                 if (ifName.equals(iface) && address.isSameAddressAs(trigger)) {
1643                     latch.countDown();
1644                 }
1645             }
1646         };
1647 
1648         mNetworkObserverRegistry.registerObserverForNonblockingCallback(observer);
1649         try {
1650             mNetd.interfaceAddAddress(iface, addr1, prefixLength);
1651             mNetd.interfaceAddAddress(iface, addr2, prefixLength);
1652             assertTrue("Trigger IP address " + addr2 + " not seen after " + TEST_TIMEOUT_MS + "ms",
1653                     latch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
1654         } finally {
1655             mNetworkObserverRegistry.unregisterObserver(observer);
1656         }
1657 
1658         // Wait for IpClient to process the addition of the address.
1659         HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1660     }
1661 
1662     private void doIPv4OnlyProvisioningAndExitWithLeftAddress() throws Exception {
1663         final long currentTime = System.currentTimeMillis();
1664         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1665                 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */,
1666                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */);
1667         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1668         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1669 
1670         // Stop IpClient and expect a final LinkProperties callback with an empty LP.
1671         mIpc.stop();
1672         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(
1673                 x -> x.getAddresses().size() == 0
1674                         && x.getRoutes().size() == 0
1675                         && x.getDnsServers().size() == 0));
1676         reset(mCb);
1677 
1678         // Pretend that something else (e.g., Tethering) used the interface and left an IP address
1679         // configured on it. When IpClient starts, it must clear this address before proceeding.
1680         // The address must be noticed before startProvisioning is called, or IpClient will
1681         // immediately declare provisioning success due to the presence of an IPv4 address.
1682         // The address must be IPv4 because IpClient clears IPv6 addresses on startup.
1683         //
1684         // TODO: once IpClient gets IP addresses directly from netlink instead of from netd, it
1685         // may be sufficient to call waitForIdle to see if IpClient has seen the address.
1686         addIpAddressAndWaitForIt(mIfaceName);
1687     }
1688 
1689     @Test
1690     public void testIpClientClearingIpAddressState() throws Exception {
1691         doIPv4OnlyProvisioningAndExitWithLeftAddress();
1692 
1693         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
1694                 .withoutIpReachabilityMonitor()
1695                 .build();
1696         mIpc.startProvisioning(config);
1697 
1698         sendBasicRouterAdvertisement(true /*waitForRs*/);
1699 
1700         // Check that the IPv4 addresses configured earlier are not in LinkProperties...
1701         ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1702         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
1703         assertFalse(captor.getValue().hasIpv4Address());
1704 
1705         // ... or configured on the interface.
1706         InterfaceConfigurationParcel cfg = mNetd.interfaceGetCfg(mIfaceName);
1707         assertEquals("0.0.0.0", cfg.ipv4Addr);
1708     }
1709 
1710     @Test
1711     public void testIpClientClearingIpAddressState_enablePreconnection() throws Exception {
1712         doIPv4OnlyProvisioningAndExitWithLeftAddress();
1713 
1714         // Enter ClearingIpAddressesState to clear the remaining IPv4 addresses and transition to
1715         // PreconnectionState instead of RunningState.
1716         startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
1717                 false /* shouldReplyRapidCommitAck */, true /* isDhcpPreConnectionEnabled */,
1718                 false /* isDhcpIpConflictDetectEnabled */);
1719         assertDiscoverPacketOnPreconnectionStart();
1720 
1721         // Force to enter RunningState.
1722         mIpc.notifyPreconnectionComplete(false /* abort */);
1723         HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
1724     }
1725 
1726     @Test
1727     public void testDhcpClientPreconnection_success() throws Exception {
1728         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1729                 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
1730                 false /* timeoutBeforePreconnectionComplete */);
1731     }
1732 
1733     @Test
1734     public void testDhcpClientPreconnection_SuccessWithoutRapidCommit() throws Exception {
1735         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1736                 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
1737                 false /* timeoutBeforePreconnectionComplete */);
1738     }
1739 
1740     @Test
1741     public void testDhcpClientPreconnection_Abort() throws Exception {
1742         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1743                 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
1744                 false /* timeoutBeforePreconnectionComplete */);
1745     }
1746 
1747     @Test
1748     public void testDhcpClientPreconnection_AbortWithoutRapiCommit() throws Exception {
1749         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1750                 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */,
1751                 false /* timeoutBeforePreconnectionComplete */);
1752     }
1753 
1754     @Test
1755     public void testDhcpClientPreconnection_TimeoutBeforeAbort() throws Exception {
1756         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1757                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1758                 true /* timeoutBeforePreconnectionComplete */);
1759     }
1760 
1761     @Test
1762     public void testDhcpClientPreconnection_TimeoutBeforeAbortWithoutRapidCommit()
1763             throws Exception {
1764         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1765                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1766                 true /* timeoutBeforePreconnectionComplete */);
1767     }
1768 
1769     @Test
1770     public void testDhcpClientPreconnection_TimeoutafterAbort() throws Exception {
1771         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1772                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1773                 false /* timeoutBeforePreconnectionComplete */);
1774     }
1775 
1776     @Test
1777     public void testDhcpClientPreconnection_TimeoutAfterAbortWithoutRapidCommit() throws Exception {
1778         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1779                 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1780                 false /* timeoutBeforePreconnectionComplete */);
1781     }
1782 
1783     @Test
1784     public void testDhcpClientPreconnection_TimeoutBeforeSuccess() throws Exception {
1785         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1786                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1787                 true /* timeoutBeforePreconnectionComplete */);
1788     }
1789 
1790     @Test
1791     public void testDhcpClientPreconnection_TimeoutBeforeSuccessWithoutRapidCommit()
1792             throws Exception {
1793         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1794                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1795                 true /* timeoutBeforePreconnectionComplete */);
1796     }
1797 
1798     @Test
1799     public void testDhcpClientPreconnection_TimeoutAfterSuccess() throws Exception {
1800         doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */,
1801                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1802                 false /* timeoutBeforePreconnectionComplete */);
1803     }
1804 
1805     @Test
1806     public void testDhcpClientPreconnection_TimeoutAfterSuccessWithoutRapidCommit()
1807             throws Exception {
1808         doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */,
1809                 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */,
1810                 false /* timeoutBeforePreconnectionComplete */);
1811     }
1812 
1813     @Test
1814     public void testDhcpClientPreconnection_WithoutLayer2InfoWhenStartingProv() throws Exception {
1815         // For FILS connection, current bssid (also l2key and cluster) is still null when
1816         // starting provisioning since the L2 link hasn't been established yet. Ensure that
1817         // IpClient won't crash even if initializing an Layer2Info class with null members.
1818         ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder()
1819                 .withoutIpReachabilityMonitor()
1820                 .withoutIPv6()
1821                 .withPreconnection()
1822                 .withLayer2Information(new Layer2Information(null /* l2key */, null /* cluster */,
1823                         null /* bssid */));
1824 
1825         mIpc.startProvisioning(prov.build());
1826         assertDiscoverPacketOnPreconnectionStart();
1827         verify(mCb).setNeighborDiscoveryOffload(true);
1828 
1829         // Force IpClient transition to RunningState from PreconnectionState.
1830         mIpc.notifyPreconnectionComplete(false /* success */);
1831         HandlerUtilsKt.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS);
1832         verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
1833     }
1834 
1835     @Test
1836     public void testDhcpDecline_conflictByArpReply() throws Exception {
1837         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
1838                 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
1839                 true /* shouldResponseArpReply */);
1840     }
1841 
1842     @Test
1843     public void testDhcpDecline_conflictByArpProbe() throws Exception {
1844         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
1845                 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
1846                 false /* shouldResponseArpReply */);
1847     }
1848 
1849     @Test
1850     public void testDhcpDecline_EnableFlagWithoutIpConflict() throws Exception {
1851         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
1852                 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
1853                 false /* shouldResponseArpReply */);
1854     }
1855 
1856     @Test
1857     public void testDhcpDecline_WithoutIpConflict() throws Exception {
1858         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
1859                 false /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */,
1860                 false /* shouldResponseArpReply */);
1861     }
1862 
1863     @Test
1864     public void testDhcpDecline_WithRapidCommitWithoutIpConflict() throws Exception {
1865         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
1866                 true /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */,
1867                 false /* shouldResponseArpReply */);
1868     }
1869 
1870     @Test
1871     public void testDhcpDecline_WithRapidCommitConflictByArpReply() throws Exception {
1872         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
1873                 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
1874                 true /* shouldResponseArpReply */);
1875     }
1876 
1877     @Test
1878     public void testDhcpDecline_WithRapidCommitConflictByArpProbe() throws Exception {
1879         doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */,
1880                 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
1881                 false /* shouldResponseArpReply */);
1882     }
1883 
1884     @Test
1885     public void testDhcpDecline_EnableFlagWithRapidCommitWithoutIpConflict() throws Exception {
1886         doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */,
1887                 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */,
1888                 false /* shouldResponseArpReply */);
1889     }
1890 
1891     @Test
1892     public void testHostname_enableConfig() throws Exception {
1893         final long currentTime = System.currentTimeMillis();
1894         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
1895                 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
1896                 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
1897                 false /* isDhcpIpConflictDetectEnabled */,
1898                 true /* isHostnameConfigurationEnabled */, TEST_HOST_NAME /* hostname */,
1899                 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */);
1900         assertEquals(2, sentPackets.size());
1901         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1902         assertHostname(true, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
1903         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1904     }
1905 
1906     @Test
1907     public void testHostname_disableConfig() throws Exception {
1908         final long currentTime = System.currentTimeMillis();
1909         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
1910                 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
1911                 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
1912                 false /* isDhcpIpConflictDetectEnabled */,
1913                 false /* isHostnameConfigurationEnabled */, TEST_HOST_NAME,
1914                 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */);
1915         assertEquals(2, sentPackets.size());
1916         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1917         assertHostname(false, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
1918         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1919     }
1920 
1921     @Test
1922     public void testHostname_enableConfigWithNullHostname() throws Exception {
1923         final long currentTime = System.currentTimeMillis();
1924         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
1925                 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
1926                 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
1927                 false /* isDhcpIpConflictDetectEnabled */,
1928                 true /* isHostnameConfigurationEnabled */, null /* hostname */,
1929                 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */);
1930         assertEquals(2, sentPackets.size());
1931         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
1932         assertHostname(true, null /* hostname */, null /* hostnameAfterTransliteration */,
1933                 sentPackets);
1934         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
1935     }
1936 
1937     private void runDhcpClientCaptivePortalApiTest(boolean featureEnabled,
1938             boolean serverSendsOption) throws Exception {
1939         startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
1940                 false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */,
1941                 false /* isDhcpIpConflictDetectEnabled */);
1942         final DhcpPacket discover = getNextDhcpPacket();
1943         assertTrue(discover instanceof DhcpDiscoverPacket);
1944         assertEquals(featureEnabled, discover.hasRequestedParam(DhcpPacket.DHCP_CAPTIVE_PORTAL));
1945 
1946         // Send Offer and handle Request -> Ack
1947         final String serverSentUrl = serverSendsOption ? TEST_CAPTIVE_PORTAL_URL : null;
1948         mPacketReader.sendResponse(buildDhcpOfferPacket(discover, TEST_LEASE_DURATION_S,
1949                 (short) TEST_DEFAULT_MTU, serverSentUrl));
1950         final int testMtu = 1345;
1951         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
1952                 false /* shouldReplyRapidCommitAck */, testMtu, serverSentUrl);
1953 
1954         final Uri expectedUrl = featureEnabled && serverSendsOption
1955                 ? Uri.parse(TEST_CAPTIVE_PORTAL_URL) : null;
1956         // Wait for LinkProperties containing DHCP-obtained info, such as MTU
1957         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
1958         verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(
1959                 argThat(lp -> lp.getMtu() == testMtu));
1960 
1961         // Ensure that the URL was set as expected in the callbacks.
1962         // Can't verify the URL up to Q as there is no such attribute in LinkProperties.
1963         if (!ShimUtils.isAtLeastR()) return;
1964         verify(mCb).onLinkPropertiesChange(captor.capture());
1965         assertTrue(captor.getAllValues().stream().anyMatch(
1966                 lp -> Objects.equals(expectedUrl, lp.getCaptivePortalApiUrl())));
1967     }
1968 
1969     @Test
1970     public void testDhcpClientCaptivePortalApiEnabled() throws Exception {
1971         // Only run the test on platforms / builds where the API is enabled
1972         assumeTrue(CaptivePortalDataShimImpl.isSupported());
1973         runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, true /* serverSendsOption */);
1974     }
1975 
1976     @Test
1977     public void testDhcpClientCaptivePortalApiEnabled_NoUrl() throws Exception {
1978         // Only run the test on platforms / builds where the API is enabled
1979         assumeTrue(CaptivePortalDataShimImpl.isSupported());
1980         runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, false /* serverSendsOption */);
1981     }
1982 
1983     @Test
1984     public void testDhcpClientCaptivePortalApiDisabled() throws Exception {
1985         // Only run the test on platforms / builds where the API is disabled
1986         assumeFalse(CaptivePortalDataShimImpl.isSupported());
1987         runDhcpClientCaptivePortalApiTest(false /* featureEnabled */, true /* serverSendsOption */);
1988     }
1989 
1990     private ScanResultInfo makeScanResultInfo(final int id, final String ssid,
1991             final String bssid, final byte[] oui, final byte type, final byte[] data) {
1992         final ByteBuffer payload = ByteBuffer.allocate(4 + data.length);
1993         payload.put(oui);
1994         payload.put(type);
1995         payload.put(data);
1996         payload.flip();
1997         final ScanResultInfo.InformationElement ie =
1998                 new ScanResultInfo.InformationElement(id /* IE id */, payload);
1999         return new ScanResultInfo(ssid, bssid, Collections.singletonList(ie));
2000     }
2001 
2002     private ScanResultInfo makeScanResultInfo(final String ssid, final String bssid) {
2003         byte[] data = new byte[10];
2004         new Random().nextBytes(data);
2005         return makeScanResultInfo(0xdd, ssid, bssid, TEST_AP_OUI, (byte) 0x06, data);
2006     }
2007 
2008     private void doUpstreamHotspotDetectionTest(final int id, final String displayName,
2009             final String ssid, final byte[] oui, final byte type, final byte[] data,
2010             final boolean expectMetered) throws Exception {
2011         final ScanResultInfo info = makeScanResultInfo(id, ssid, TEST_DEFAULT_BSSID, oui, type,
2012                 data);
2013         final long currentTime = System.currentTimeMillis();
2014         final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */,
2015                 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
2016                 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
2017                 false /* isDhcpIpConflictDetectEnabled */,
2018                 false /* isHostnameConfigurationEnabled */, null /* hostname */,
2019                 null /* captivePortalApiUrl */, displayName, info /* scanResultInfo */);
2020         assertEquals(2, sentPackets.size());
2021         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2022 
2023         ArgumentCaptor<DhcpResultsParcelable> captor =
2024                 ArgumentCaptor.forClass(DhcpResultsParcelable.class);
2025         verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture());
2026         DhcpResults lease = fromStableParcelable(captor.getValue());
2027         assertNotNull(lease);
2028         assertEquals(lease.getIpAddress().getAddress(), CLIENT_ADDR);
2029         assertEquals(lease.getGateway(), SERVER_ADDR);
2030         assertEquals(1, lease.getDnsServers().size());
2031         assertTrue(lease.getDnsServers().contains(SERVER_ADDR));
2032         assertEquals(lease.getServerAddress(), SERVER_ADDR);
2033         assertEquals(lease.getMtu(), TEST_DEFAULT_MTU);
2034 
2035         if (expectMetered) {
2036             assertEquals(lease.vendorInfo, DhcpPacket.VENDOR_INFO_ANDROID_METERED);
2037         } else {
2038             assertNull(lease.vendorInfo);
2039         }
2040 
2041         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2042     }
2043 
2044     @Test
2045     public void testUpstreamHotspotDetection() throws Exception {
2046         byte[] data = new byte[10];
2047         new Random().nextBytes(data);
2048         doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid",
2049                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2050                 true /* expectMetered */);
2051     }
2052 
2053     @Test
2054     public void testUpstreamHotspotDetection_incorrectIeId() throws Exception {
2055         byte[] data = new byte[10];
2056         new Random().nextBytes(data);
2057         doUpstreamHotspotDetectionTest(0xdc, "\"ssid\"", "ssid",
2058                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2059                 false /* expectMetered */);
2060     }
2061 
2062     @Test
2063     public void testUpstreamHotspotDetection_incorrectOUI() throws Exception {
2064         byte[] data = new byte[10];
2065         new Random().nextBytes(data);
2066         doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid",
2067                 new byte[] { (byte) 0x00, (byte) 0x1A, (byte) 0x11 }, (byte) 0x06, data,
2068                 false /* expectMetered */);
2069     }
2070 
2071     @Test
2072     public void testUpstreamHotspotDetection_incorrectSsid() throws Exception {
2073         byte[] data = new byte[10];
2074         new Random().nextBytes(data);
2075         doUpstreamHotspotDetectionTest(0xdd, "\"another ssid\"", "ssid",
2076                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2077                 false /* expectMetered */);
2078     }
2079 
2080     @Test
2081     public void testUpstreamHotspotDetection_incorrectType() throws Exception {
2082         byte[] data = new byte[10];
2083         new Random().nextBytes(data);
2084         doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid",
2085                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x0a, data,
2086                 false /* expectMetered */);
2087     }
2088 
2089     @Test
2090     public void testUpstreamHotspotDetection_zeroLengthData() throws Exception {
2091         byte[] data = new byte[0];
2092         doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid",
2093                 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data,
2094                 true /* expectMetered */);
2095     }
2096 
2097     private void doDhcpRoamingTest(final boolean hasMismatchedIpAddress, final String displayName,
2098             final String ssid, final String bssid, final boolean expectRoaming) throws Exception {
2099         long currentTime = System.currentTimeMillis();
2100         final ScanResultInfo scanResultInfo = makeScanResultInfo(ssid, bssid);
2101 
2102         doAnswer(invocation -> {
2103             // we don't rely on the Init-Reboot state to renew previous cached IP lease.
2104             // Just return null and force state machine enter INIT state.
2105             final String l2Key = invocation.getArgument(0);
2106             ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1))
2107                     .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null);
2108             return null;
2109         }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any());
2110 
2111         performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2112                 true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */,
2113                 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */,
2114                 true /* isHostnameConfigurationEnabled */, null /* hostname */,
2115                 null /* captivePortalApiUrl */, displayName, scanResultInfo);
2116         verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
2117         assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
2118 
2119         // simulate the roaming by updating bssid.
2120         final Layer2InformationParcelable roamingInfo = new Layer2InformationParcelable();
2121         roamingInfo.bssid = MacAddress.fromString(TEST_DHCP_ROAM_BSSID);
2122         roamingInfo.l2Key = TEST_DHCP_ROAM_L2KEY;
2123         roamingInfo.cluster = TEST_DHCP_ROAM_CLUSTER;
2124         mIpc.updateLayer2Information(roamingInfo);
2125 
2126         currentTime = System.currentTimeMillis();
2127         reset(mIpMemoryStore);
2128         reset(mCb);
2129         if (!expectRoaming) {
2130             assertIpMemoryNeverStoreNetworkAttributes();
2131             return;
2132         }
2133         // check DHCPREQUEST broadcast sent to renew IP address.
2134         DhcpPacket packet;
2135         packet = getNextDhcpPacket();
2136         assertTrue(packet instanceof DhcpRequestPacket);
2137         assertEquals(packet.mClientIp, CLIENT_ADDR);    // client IP
2138         assertNull(packet.mRequestedIp);                // requested IP option
2139         assertNull(packet.mServerIdentifier);           // server ID
2140 
2141         mPacketReader.sendResponse(buildDhcpAckPacket(packet,
2142                 hasMismatchedIpAddress ? CLIENT_ADDR_NEW : CLIENT_ADDR, TEST_LEASE_DURATION_S,
2143                 (short) TEST_DEFAULT_MTU, false /* rapidcommit */, null /* captivePortalUrl */));
2144         HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
2145         if (hasMismatchedIpAddress) {
2146             // notifyFailure
2147             ArgumentCaptor<DhcpResultsParcelable> captor =
2148                     ArgumentCaptor.forClass(DhcpResultsParcelable.class);
2149             verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture());
2150             DhcpResults lease = fromStableParcelable(captor.getValue());
2151             assertNull(lease);
2152 
2153             // roll back to INIT state.
2154             packet = getNextDhcpPacket();
2155             assertTrue(packet instanceof DhcpDiscoverPacket);
2156         } else {
2157             assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime,
2158                     TEST_DEFAULT_MTU);
2159         }
2160     }
2161 
2162     @Test
2163     public void testDhcpRoaming() throws Exception {
2164         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
2165                 TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */);
2166     }
2167 
2168     @Test
2169     public void testDhcpRoaming_invalidBssid() throws Exception {
2170         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
2171                 TEST_DHCP_ROAM_SSID, TEST_DHCP_ROAM_BSSID, false /* expectRoaming */);
2172     }
2173 
2174     @Test
2175     public void testDhcpRoaming_invalidSsid() throws Exception {
2176         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
2177                 TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID, false /* expectRoaming */);
2178     }
2179 
2180     @Test
2181     public void testDhcpRoaming_invalidDisplayName() throws Exception {
2182         doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"test-ssid\"" /* display name */,
2183                 TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, false /* expectRoaming */);
2184     }
2185 
2186     @Test
2187     public void testDhcpRoaming_mismatchedLeasedIpAddress() throws Exception {
2188         doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
2189                 TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */);
2190     }
2191 
2192     private void doDualStackProvisioning() throws Exception {
2193         when(mCm.shouldAvoidBadWifi()).thenReturn(true);
2194 
2195         final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
2196                 .withoutIpReachabilityMonitor()
2197                 .build();
2198         // Accelerate DHCP handshake to shorten test duration, not strictly necessary.
2199         mDependencies.setDhcpRapidCommitEnabled(true);
2200         mIpc.startProvisioning(config);
2201 
2202         final InOrder inOrder = inOrder(mCb);
2203         final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
2204         final String dnsServer = "2001:4860:4860::64";
2205         final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64");
2206         final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer);
2207         final ByteBuffer ra = buildRaPacket(pio, rdnss);
2208 
2209         doIpv6OnlyProvisioning(inOrder, ra);
2210 
2211         // Start IPv4 provisioning and wait until entire provisioning completes.
2212         handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
2213                 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
2214         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> {
2215             if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false;
2216             lpFuture.complete(x);
2217             return true;
2218         }));
2219 
2220         final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
2221         assertNotNull(lp);
2222         assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
2223         assertTrue(lp.getDnsServers().contains(SERVER_ADDR));
2224 
2225         reset(mCb);
2226     }
2227 
2228     @Test
2229     public void testIgnoreIpv6ProvisioningLoss() throws Exception {
2230         doDualStackProvisioning();
2231 
2232         final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
2233 
2234         // Send RA with 0-lifetime and wait until all IPv6-related default route and DNS servers
2235         // have been removed, then verify if there is IPv4-only info left in the LinkProperties.
2236         sendRouterAdvertisementWithZeroLifetime();
2237         verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(
2238                 argThat(x -> {
2239                     final boolean isOnlyIPv4Provisioned = (x.getLinkAddresses().size() == 1
2240                             && x.getDnsServers().size() == 1
2241                             && x.getAddresses().get(0) instanceof Inet4Address
2242                             && x.getDnsServers().get(0) instanceof Inet4Address);
2243 
2244                     if (!isOnlyIPv4Provisioned) return false;
2245                     lpFuture.complete(x);
2246                     return true;
2247                 }));
2248         final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
2249         assertNotNull(lp);
2250         assertEquals(lp.getAddresses().get(0), CLIENT_ADDR);
2251         assertEquals(lp.getDnsServers().get(0), SERVER_ADDR);
2252     }
2253 
2254     @Test
2255     public void testDualStackProvisioning() throws Exception {
2256         doDualStackProvisioning();
2257 
2258         verify(mCb, never()).onProvisioningFailure(any());
2259     }
2260 }
2261