1 /*
2  * Copyright (C) 2015 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.dhcp;
18 
19 import static android.net.dhcp.DhcpPacket.CONFIG_MINIMUM_LEASE;
20 import static android.net.dhcp.DhcpPacket.DEFAULT_MINIMUM_LEASE;
21 import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
22 import static android.net.dhcp.DhcpPacket.DHCP_CAPTIVE_PORTAL;
23 import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
24 import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
25 import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_SEARCHLIST;
26 import static android.net.dhcp.DhcpPacket.DHCP_IPV6_ONLY_PREFERRED;
27 import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
28 import static android.net.dhcp.DhcpPacket.DHCP_MTU;
29 import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
30 import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
31 import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
32 import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
33 import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
34 import static android.net.dhcp.DhcpPacket.INADDR_ANY;
35 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
36 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
37 import static android.net.util.SocketUtils.makePacketSocketAddress;
38 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
39 import static android.system.OsConstants.AF_INET;
40 import static android.system.OsConstants.AF_PACKET;
41 import static android.system.OsConstants.ETH_P_ARP;
42 import static android.system.OsConstants.ETH_P_IP;
43 import static android.system.OsConstants.IPPROTO_UDP;
44 import static android.system.OsConstants.SOCK_DGRAM;
45 import static android.system.OsConstants.SOCK_NONBLOCK;
46 import static android.system.OsConstants.SOCK_RAW;
47 import static android.system.OsConstants.SOL_SOCKET;
48 import static android.system.OsConstants.SO_BROADCAST;
49 import static android.system.OsConstants.SO_RCVBUF;
50 import static android.system.OsConstants.SO_REUSEADDR;
51 
52 import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST;
53 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
54 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
55 import static com.android.net.module.util.NetworkStackConstants.IPV4_CONFLICT_ANNOUNCE_NUM;
56 import static com.android.net.module.util.NetworkStackConstants.IPV4_CONFLICT_PROBE_NUM;
57 import static com.android.net.module.util.SocketUtils.closeSocketQuietly;
58 import static com.android.networkstack.util.NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION;
59 import static com.android.networkstack.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION;
60 import static com.android.networkstack.util.NetworkStackUtils.DHCP_SLOW_RETRANSMISSION_VERSION;
61 
62 import android.content.Context;
63 import android.net.DhcpResults;
64 import android.net.InetAddresses;
65 import android.net.Layer2PacketParcelable;
66 import android.net.MacAddress;
67 import android.net.NetworkStackIpMemoryStore;
68 import android.net.TrafficStats;
69 import android.net.ip.IIpClient;
70 import android.net.ip.IpClient;
71 import android.net.ipmemorystore.NetworkAttributes;
72 import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
73 import android.net.ipmemorystore.OnStatusListener;
74 import android.net.metrics.DhcpClientEvent;
75 import android.net.metrics.DhcpErrorEvent;
76 import android.net.metrics.IpConnectivityLog;
77 import android.net.networkstack.aidl.dhcp.DhcpOption;
78 import android.net.util.HostnameTransliterator;
79 import android.net.util.SocketUtils;
80 import android.os.Handler;
81 import android.os.Message;
82 import android.os.PowerManager;
83 import android.os.SystemClock;
84 import android.provider.Settings;
85 import android.stats.connectivity.DhcpFeature;
86 import android.system.ErrnoException;
87 import android.system.Os;
88 import android.util.EventLog;
89 import android.util.Log;
90 import android.util.SparseArray;
91 
92 import androidx.annotation.NonNull;
93 import androidx.annotation.Nullable;
94 
95 import com.android.internal.annotations.VisibleForTesting;
96 import com.android.internal.util.HexDump;
97 import com.android.internal.util.MessageUtils;
98 import com.android.internal.util.State;
99 import com.android.internal.util.StateMachine;
100 import com.android.internal.util.WakeupMessage;
101 import com.android.net.module.util.DeviceConfigUtils;
102 import com.android.net.module.util.InterfaceParams;
103 import com.android.net.module.util.NetworkStackConstants;
104 import com.android.net.module.util.PacketReader;
105 import com.android.net.module.util.arp.ArpPacket;
106 import com.android.networkstack.R;
107 import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
108 import com.android.networkstack.apishim.SocketUtilsShimImpl;
109 import com.android.networkstack.metrics.IpProvisioningMetrics;
110 import com.android.networkstack.util.NetworkStackUtils;
111 
112 import java.io.ByteArrayOutputStream;
113 import java.io.FileDescriptor;
114 import java.io.IOException;
115 import java.net.Inet4Address;
116 import java.net.SocketAddress;
117 import java.net.SocketException;
118 import java.nio.ByteBuffer;
119 import java.util.Arrays;
120 import java.util.List;
121 import java.util.Random;
122 
123 /**
124  * A DHCPv4 client.
125  *
126  * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android
127  * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine.
128  *
129  * TODO:
130  *
131  * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour).
132  * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not
133  *   do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a
134  *   given SSID), it requests the last-leased IP address on the same interface, causing a delay if
135  *   the server NAKs or a timeout if it doesn't.
136  *
137  * Known differences from current behaviour:
138  *
139  * - Does not request the "static routes" option.
140  * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now.
141  * - Requests the "broadcast" option, but does nothing with it.
142  * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0).
143  *
144  * @hide
145  */
146 public class DhcpClient extends StateMachine {
147 
148     private static final String TAG = "DhcpClient";
149     private static final boolean DBG = true;
150     private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
151     private static final boolean STATE_DBG = Log.isLoggable(TAG, Log.DEBUG);
152     private static final boolean MSG_DBG = Log.isLoggable(TAG, Log.DEBUG);
153     private static final boolean PACKET_DBG = Log.isLoggable(TAG, Log.DEBUG);
154 
155     // Metrics events: must be kept in sync with server-side aggregation code.
156     /** Represents transitions from DhcpInitState to DhcpBoundState */
157     private static final String EVENT_INITIAL_BOUND = "InitialBoundState";
158     /** Represents transitions from and to DhcpBoundState via DhcpRenewingState */
159     private static final String EVENT_RENEWING_BOUND = "RenewingBoundState";
160 
161     // Timers and timeouts.
162     private static final int SECONDS = 1000;
163     private static final int FIRST_TIMEOUT_MS         =   1 * SECONDS;
164     private static final int MAX_TIMEOUT_MS           = 512 * SECONDS;
165     private static final int IPMEMORYSTORE_TIMEOUT_MS =   1 * SECONDS;
166     private static final int DHCP_INITREBOOT_TIMEOUT_MS = 5 * SECONDS;
167 
168     // The waiting time to restart the DHCP configuration process after broadcasting a
169     // DHCPDECLINE message, (RFC2131 3.1.5 describes client SHOULD wait a minimum of 10
170     // seconds to avoid excessive traffic, but it's too long).
171     @VisibleForTesting
172     public static final String DHCP_RESTART_CONFIG_DELAY = "dhcp_restart_configuration_delay";
173     private static final int DEFAULT_DHCP_RESTART_CONFIG_DELAY_MS = 1 * SECONDS;
174     private static final int MAX_DHCP_CLIENT_RESTART_CONFIG_DELAY_MS = 10 * SECONDS;
175 
176     // Initial random delay before sending first ARP probe.
177     @VisibleForTesting
178     public static final String ARP_FIRST_PROBE_DELAY_MS = "arp_first_probe_delay";
179     private static final int DEFAULT_ARP_FIRST_PROBE_DELAY_MS = 100;
180     private static final int MAX_ARP_FIRST_PROBE_DELAY_MS = 1 * SECONDS;
181 
182     // Minimum delay until retransmitting the probe. The probe will be retransmitted after a
183     // random number of milliseconds in the range ARP_PROBE_MIN_MS and ARP_PROBE_MAX_MS.
184     @VisibleForTesting
185     public static final String ARP_PROBE_MIN_MS = "arp_probe_min";
186     private static final int DEFAULT_ARP_PROBE_MIN_MS = 100;
187     private static final int MAX_ARP_PROBE_MIN_MS = 1 * SECONDS;
188 
189     // Maximum delay until retransmitting the probe.
190     @VisibleForTesting
191     public static final String ARP_PROBE_MAX_MS = "arp_probe_max";
192     private static final int DEFAULT_ARP_PROBE_MAX_MS = 300;
193     private static final int MAX_ARP_PROBE_MAX_MS = 2 * SECONDS;
194 
195     // Initial random delay before sending first ARP Announcement after completing Probe packet
196     // transmission.
197     @VisibleForTesting
198     public static final String ARP_FIRST_ANNOUNCE_DELAY_MS = "arp_first_announce_delay";
199     private static final int DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS = 100;
200     private static final int MAX_ARP_FIRST_ANNOUNCE_DELAY_MS = 2 * SECONDS;
201 
202     // Time between retransmitting ARP Announcement packets.
203     @VisibleForTesting
204     public static final String ARP_ANNOUNCE_INTERVAL_MS = "arp_announce_interval";
205     private static final int DEFAULT_ARP_ANNOUNCE_INTERVAL_MS = 100;
206     private static final int MAX_ARP_ANNOUNCE_INTERVAL_MS = 2 * SECONDS;
207 
208     // Max conflict count before configuring interface with declined IP address anyway.
209     private static final int MAX_CONFLICTS_COUNT = 2;
210 
211     // This is not strictly needed, since the client is asynchronous and implements exponential
212     // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
213     // a blocking operation with a 30-second timeout. We pick 18 seconds so we can send packets at
214     // t=0, t=1, t=3, t=7, t=16, allowing for 10% jitter.
215     private static final int DHCP_TIMEOUT_MS    =  36 * SECONDS;
216 
217     // DhcpClient uses IpClient's handler.
218     private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
219 
220     // Below constants are picked up by MessageUtils and exempt from ProGuard optimization.
221     /* Commands from controller to start/stop DHCP */
222     public static final int CMD_START_DHCP                  = PUBLIC_BASE + 1;
223     public static final int CMD_STOP_DHCP                   = PUBLIC_BASE + 2;
224 
225     /* Notification from DHCP state machine prior to DHCP discovery/renewal */
226     public static final int CMD_PRE_DHCP_ACTION             = PUBLIC_BASE + 3;
227     /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
228      * success/failure */
229     public static final int CMD_POST_DHCP_ACTION            = PUBLIC_BASE + 4;
230     /* Notification from DHCP state machine before quitting */
231     public static final int CMD_ON_QUIT                     = PUBLIC_BASE + 5;
232 
233     /* Command from controller to indicate DHCP discovery/renewal can continue
234      * after pre DHCP action is complete */
235     public static final int CMD_PRE_DHCP_ACTION_COMPLETE    = PUBLIC_BASE + 6;
236 
237     /* Command and event notification to/from IpManager requesting the setting
238      * (or clearing) of an IPv4 LinkAddress.
239      */
240     public static final int CMD_CLEAR_LINKADDRESS           = PUBLIC_BASE + 7;
241     public static final int CMD_CONFIGURE_LINKADDRESS       = PUBLIC_BASE + 8;
242     public static final int EVENT_LINKADDRESS_CONFIGURED    = PUBLIC_BASE + 9;
243 
244     // Command to IpClient starting/aborting preconnection process.
245     public static final int CMD_START_PRECONNECTION         = PUBLIC_BASE + 10;
246     public static final int CMD_ABORT_PRECONNECTION         = PUBLIC_BASE + 11;
247 
248     // Command to rebind the leased IPv4 address on L2 roaming happened.
249     public static final int CMD_REFRESH_LINKADDRESS         = PUBLIC_BASE + 12;
250 
251     /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
252     public static final int DHCP_SUCCESS = 1;
253     public static final int DHCP_FAILURE = 2;
254     public static final int DHCP_IPV6_ONLY = 3;
255     public static final int DHCP_REFRESH_FAILURE = 4;
256 
257     // Internal messages.
258     private static final int PRIVATE_BASE         = IpClient.DHCPCLIENT_CMD_BASE + 100;
259     private static final int CMD_KICK             = PRIVATE_BASE + 1;
260     private static final int CMD_RECEIVED_PACKET  = PRIVATE_BASE + 2;
261     @VisibleForTesting
262     public static final int CMD_TIMEOUT           = PRIVATE_BASE + 3;
263     private static final int CMD_RENEW_DHCP       = PRIVATE_BASE + 4;
264     private static final int CMD_REBIND_DHCP      = PRIVATE_BASE + 5;
265     private static final int CMD_EXPIRE_DHCP      = PRIVATE_BASE + 6;
266     private static final int EVENT_CONFIGURATION_TIMEOUT   = PRIVATE_BASE + 7;
267     private static final int EVENT_CONFIGURATION_OBTAINED  = PRIVATE_BASE + 8;
268     private static final int EVENT_CONFIGURATION_INVALID   = PRIVATE_BASE + 9;
269     private static final int EVENT_IP_CONFLICT             = PRIVATE_BASE + 10;
270     private static final int CMD_ARP_PROBE        = PRIVATE_BASE + 11;
271     private static final int CMD_ARP_ANNOUNCEMENT = PRIVATE_BASE + 12;
272 
273     // constant to represent this DHCP lease has been expired.
274     @VisibleForTesting
275     public static final long EXPIRED_LEASE = 1L;
276 
277     // For message logging.
278     private static final Class[] sMessageClasses = { DhcpClient.class };
279     private static final SparseArray<String> sMessageNames =
280             MessageUtils.findMessageNames(sMessageClasses);
281 
282     // DHCP parameters that we request by default.
283     @VisibleForTesting
284     /* package */ static final byte[] DEFAULT_REQUESTED_PARAMS = new byte[] {
285         DHCP_SUBNET_MASK,
286         DHCP_ROUTER,
287         DHCP_DNS_SERVER,
288         DHCP_DOMAIN_NAME,
289         DHCP_MTU,
290         DHCP_BROADCAST_ADDRESS,  // TODO: currently ignored.
291         DHCP_LEASE_TIME,
292         DHCP_RENEWAL_TIME,
293         DHCP_REBINDING_TIME,
294         DHCP_VENDOR_INFO,
295     };
296 
297     @NonNull
getRequestedParams()298     private byte[] getRequestedParams() {
299         // Set an initial size large enough for all optional parameters that we might request.
300         // mCreatorId + the size is changed
301         final int numOptionalParams;
302         if (mConfiguration.isWifiManagedProfile) {
303             numOptionalParams = 3 + mConfiguration.options.size();
304         } else {
305             numOptionalParams = 2 + mConfiguration.options.size();
306         }
307 
308         final ByteArrayOutputStream params =
309                 new ByteArrayOutputStream(DEFAULT_REQUESTED_PARAMS.length + numOptionalParams);
310         params.write(DEFAULT_REQUESTED_PARAMS, 0, DEFAULT_REQUESTED_PARAMS.length);
311         if (isCapportApiEnabled()) {
312             params.write(DHCP_CAPTIVE_PORTAL);
313         }
314         params.write(DHCP_IPV6_ONLY_PREFERRED);
315         // Customized DHCP options to be put in PRL.
316         for (DhcpOption option : mConfiguration.options) {
317             if (option.value == null) params.write(option.type);
318         }
319         // Check if the target network is managed by user.
320         if (mConfiguration.isWifiManagedProfile) {
321             params.write(DHCP_DOMAIN_SEARCHLIST);
322         }
323         return params.toByteArray();
324     }
325 
isCapportApiEnabled()326     private static boolean isCapportApiEnabled() {
327         return CaptivePortalDataShimImpl.isSupported();
328     }
329 
330     // DHCP flag that means "yes, we support unicast."
331     private static final boolean DO_UNICAST   = false;
332 
333     // System services / libraries we use.
334     private final Context mContext;
335     private final Random mRandom;
336     private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
337     @NonNull
338     private final IpProvisioningMetrics mMetrics;
339 
340     // We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
341     // be off-link as well as on-link).
342     private FileDescriptor mUdpSock;
343 
344     // State variables.
345     private final StateMachine mController;
346     private final WakeupMessage mKickAlarm;
347     private final WakeupMessage mTimeoutAlarm;
348     private final WakeupMessage mRenewAlarm;
349     private final WakeupMessage mRebindAlarm;
350     private final WakeupMessage mExpiryAlarm;
351     private final String mIfaceName;
352 
353     private boolean mRegisteredForPreDhcpNotification;
354     private InterfaceParams mIface;
355     // TODO: MacAddress-ify more of this class hierarchy.
356     private byte[] mHwAddr;
357     private SocketAddress mInterfaceBroadcastAddr;
358     private int mTransactionId;
359     private long mTransactionStartMillis;
360     private DhcpResults mDhcpLease;
361     private long mDhcpLeaseExpiry;
362     private long mT2;
363     private DhcpResults mOffer;
364     private Configuration mConfiguration;
365     private Inet4Address mLastAssignedIpv4Address;
366     private int mConflictCount;
367     private long mLastAssignedIpv4AddressExpiry;
368     private Dependencies mDependencies;
369     @Nullable
370     private DhcpPacketHandler mDhcpPacketHandler;
371     @NonNull
372     private final NetworkStackIpMemoryStore mIpMemoryStore;
373     @Nullable
374     private final String mHostname;
375 
376     // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
377     private long mLastInitEnterTime;
378     private long mLastBoundExitTime;
379 
380     // 32-bit unsigned integer used to indicate the number of milliseconds the DHCP client should
381     // disable DHCPv4.
382     private long mIpv6OnlyWaitTimeMs;
383 
384     // States.
385     private State mStoppedState = new StoppedState();
386     private State mDhcpState = new DhcpState();
387     private State mDhcpInitState = new DhcpInitState();
388     private State mDhcpPreconnectingState = new DhcpPreconnectingState();
389     private State mDhcpSelectingState = new DhcpSelectingState();
390     private State mDhcpRequestingState = new DhcpRequestingState();
391     private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
392     private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
393     private State mDhcpBoundState = new DhcpBoundState();
394     private State mDhcpRenewingState = new DhcpRenewingState();
395     private State mDhcpRebindingState = new DhcpRebindingState();
396     private State mDhcpInitRebootState = new DhcpInitRebootState();
397     private State mDhcpRebootingState = new DhcpRebootingState();
398     private State mObtainingConfigurationState = new ObtainingConfigurationState();
399     private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
400     private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
401     private State mWaitBeforeObtainingConfigurationState =
402             new WaitBeforeObtainingConfigurationState(mObtainingConfigurationState);
403     private State mIpAddressConflictDetectingState = new IpAddressConflictDetectingState();
404     private State mDhcpDecliningState = new DhcpDecliningState();
405     private State mIpv6OnlyWaitState = new Ipv6OnlyWaitState();
406     private State mDhcpRefreshingAddressState = new DhcpRefreshingAddressState();
407 
makeWakeupMessage(String cmdName, int cmd)408     private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
409         cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
410         return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
411     }
412 
413     /**
414      * Encapsulates DhcpClient depencencies that's used for unit testing and
415      * integration testing.
416      */
417     public static class Dependencies {
418         private final NetworkStackIpMemoryStore mNetworkStackIpMemoryStore;
419         private final IpProvisioningMetrics mMetrics;
420 
Dependencies(NetworkStackIpMemoryStore store, IpProvisioningMetrics metrics)421         public Dependencies(NetworkStackIpMemoryStore store, IpProvisioningMetrics metrics) {
422             mNetworkStackIpMemoryStore = store;
423             mMetrics = metrics;
424         }
425 
426         /**
427          * Get the configuration from RRO to check whether or not to send hostname option in
428          * DHCPDISCOVER/DHCPREQUEST message.
429          */
getSendHostnameOverlaySetting(final Context context)430         public boolean getSendHostnameOverlaySetting(final Context context) {
431             return context.getResources().getBoolean(R.bool.config_dhcp_client_hostname);
432         }
433 
434         /**
435          * Get the device name from system settings.
436          */
getDeviceName(final Context context)437         public String getDeviceName(final Context context) {
438             return Settings.Global.getString(context.getContentResolver(),
439                     Settings.Global.DEVICE_NAME);
440         }
441 
442         /**
443          * Get a IpMemoryStore instance.
444          */
getIpMemoryStore()445         public NetworkStackIpMemoryStore getIpMemoryStore() {
446             return mNetworkStackIpMemoryStore;
447         }
448 
449         /**
450          * Get a IpProvisioningMetrics instance.
451          */
getIpProvisioningMetrics()452         public IpProvisioningMetrics getIpProvisioningMetrics() {
453             return mMetrics;
454         }
455 
456         /**
457          * Return whether a feature guarded by a feature flag is enabled.
458          * @see DeviceConfigUtils#isNetworkStackFeatureEnabled(Context, String)
459          */
isFeatureEnabled(final Context context, final String name)460         public boolean isFeatureEnabled(final Context context, final String name) {
461             return DeviceConfigUtils.isNetworkStackFeatureEnabled(context, name);
462         }
463 
464         /**
465          * Check whether one specific feature is not disabled.
466          * @see DeviceConfigUtils#isNetworkStackFeatureNotChickenedOut(Context, String)
467          */
isFeatureNotChickenedOut(final Context context, final String name)468         public boolean isFeatureNotChickenedOut(final Context context, final String name) {
469             return DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut(context, name);
470         }
471 
472         /**
473          * Get the Integer value of relevant DeviceConfig properties of Connectivity namespace.
474          */
getIntDeviceConfig(final String name, int minimumValue, int maximumValue, int defaultValue)475         public int getIntDeviceConfig(final String name, int minimumValue, int maximumValue,
476                 int defaultValue) {
477             return DeviceConfigUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
478                     name, minimumValue, maximumValue, defaultValue);
479         }
480 
481         /**
482          * Get the Integer value of relevant DeviceConfig properties of Connectivity namespace.
483          */
getIntDeviceConfig(final String name, int defaultValue)484         public int getIntDeviceConfig(final String name, int defaultValue) {
485             return DeviceConfigUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
486                     name, defaultValue);
487         }
488 
489         /**
490          * Get a new wake lock to force CPU keeping awake when transmitting packets or waiting
491          * for timeout.
492          */
getWakeLock(final PowerManager powerManager)493         public PowerManager.WakeLock getWakeLock(final PowerManager powerManager) {
494             return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
495         }
496     }
497 
498     // TODO: Take an InterfaceParams instance instead of an interface name String.
DhcpClient(Context context, StateMachine controller, String iface, Dependencies deps)499     private DhcpClient(Context context, StateMachine controller, String iface,
500             Dependencies deps) {
501         super(TAG, controller.getHandler());
502 
503         mDependencies = deps;
504         mContext = context;
505         mController = controller;
506         mIfaceName = iface;
507         mIpMemoryStore = deps.getIpMemoryStore();
508         mMetrics = deps.getIpProvisioningMetrics();
509 
510         // CHECKSTYLE:OFF IndentationCheck
511         addState(mStoppedState);
512         addState(mDhcpState);
513             addState(mDhcpInitState, mDhcpState);
514             addState(mWaitBeforeStartState, mDhcpState);
515             addState(mWaitBeforeObtainingConfigurationState, mDhcpState);
516             addState(mDhcpPreconnectingState, mDhcpState);
517             addState(mObtainingConfigurationState, mDhcpState);
518             addState(mDhcpSelectingState, mDhcpState);
519             addState(mDhcpRequestingState, mDhcpState);
520             addState(mIpAddressConflictDetectingState, mDhcpState);
521             addState(mIpv6OnlyWaitState, mDhcpState);
522             addState(mDhcpHaveLeaseState, mDhcpState);
523                 addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
524                 addState(mDhcpBoundState, mDhcpHaveLeaseState);
525                 addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
526                 addState(mDhcpRenewingState, mDhcpHaveLeaseState);
527                 addState(mDhcpRebindingState, mDhcpHaveLeaseState);
528                 addState(mDhcpDecliningState, mDhcpHaveLeaseState);
529                 addState(mDhcpRefreshingAddressState, mDhcpHaveLeaseState);
530             addState(mDhcpInitRebootState, mDhcpState);
531             addState(mDhcpRebootingState, mDhcpState);
532         // CHECKSTYLE:ON IndentationCheck
533 
534         setInitialState(mStoppedState);
535 
536         mRandom = new Random();
537 
538         // Used to schedule packet retransmissions.
539         mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
540         // Used to time out PacketRetransmittingStates.
541         mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
542         // Used to schedule DHCP reacquisition.
543         mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
544         mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
545         mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
546 
547         mHostname = new HostnameTransliterator().transliterate(deps.getDeviceName(mContext));
548         mMetrics.setHostnameTransinfo(deps.getSendHostnameOverlaySetting(context),
549                 mHostname != null);
550     }
551 
552     @Nullable
maybeGetHostnameForSending()553     private String maybeGetHostnameForSending() {
554         boolean sendHostname = mDependencies.getSendHostnameOverlaySetting(mContext);
555         if (mConfiguration != null
556                 && mConfiguration.hostnameSetting != IIpClient.HOSTNAME_SETTING_UNSET) {
557             sendHostname = mConfiguration.hostnameSetting == IIpClient.HOSTNAME_SETTING_SEND;
558         }
559         return sendHostname ? mHostname : null;
560     }
561 
registerForPreDhcpNotification()562     public void registerForPreDhcpNotification() {
563         mRegisteredForPreDhcpNotification = true;
564     }
565 
makeDhcpClient( Context context, StateMachine controller, InterfaceParams ifParams, Dependencies deps)566     public static DhcpClient makeDhcpClient(
567             Context context, StateMachine controller, InterfaceParams ifParams,
568             Dependencies deps) {
569         DhcpClient client = new DhcpClient(context, controller, ifParams.name, deps);
570         client.mIface = ifParams;
571         client.start();
572         return client;
573     }
574 
575     /**
576      * check whether or not to support DHCP Rapid Commit option.
577      */
isDhcpRapidCommitEnabled()578     public boolean isDhcpRapidCommitEnabled() {
579         return mDependencies.isFeatureNotChickenedOut(mContext, DHCP_RAPID_COMMIT_VERSION);
580     }
581 
582     /**
583      * check whether or not to support IP address conflict detection and DHCPDECLINE.
584      */
isDhcpIpConflictDetectEnabled()585     public boolean isDhcpIpConflictDetectEnabled() {
586         return mDependencies.isFeatureEnabled(mContext, DHCP_IP_CONFLICT_DETECT_VERSION);
587     }
588 
589     /**
590      * Check whether to adopt slow DHCPREQUEST retransmission approach in Renewing/Rebinding state
591      * suggested in RFC2131 section 4.4.5.
592      */
isSlowRetransmissionEnabled()593     public boolean isSlowRetransmissionEnabled() {
594         return mDependencies.isFeatureEnabled(mContext, DHCP_SLOW_RETRANSMISSION_VERSION);
595     }
596 
recordMetricEnabledFeatures()597     private void recordMetricEnabledFeatures() {
598         mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT);
599         if (isDhcpRapidCommitEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_RAPIDCOMMIT);
600         if (isDhcpIpConflictDetectEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_DAD);
601         if (mConfiguration.isPreconnectionEnabled) {
602             mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_FILS);
603         }
604     }
605 
confirmDhcpLease(DhcpPacket packet, DhcpResults results)606     private void confirmDhcpLease(DhcpPacket packet, DhcpResults results) {
607         setDhcpLeaseExpiry(packet);
608         acceptDhcpResults(results, "Confirmed");
609     }
610 
initInterface()611     private boolean initInterface() {
612         if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
613         if (mIface == null) {
614             Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
615             return false;
616         }
617 
618         mHwAddr = mIface.macAddr.toByteArray();
619         mInterfaceBroadcastAddr = SocketUtilsShimImpl.newInstance().makePacketSocketAddress(
620                 ETH_P_IP, mIface.index, DhcpPacket.ETHER_BROADCAST);
621         return true;
622     }
623 
startNewTransaction()624     private void startNewTransaction() {
625         mTransactionId = mRandom.nextInt();
626         mTransactionStartMillis = SystemClock.elapsedRealtime();
627     }
628 
initUdpSocket()629     private boolean initUdpSocket() {
630         final int oldTag = TrafficStats.getAndSetThreadStatsTag(
631                 NetworkStackConstants.TAG_SYSTEM_DHCP);
632         try {
633             mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
634             SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
635             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
636             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
637             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
638             Os.bind(mUdpSock, IPV4_ADDR_ANY, DhcpPacket.DHCP_CLIENT);
639         } catch (SocketException | ErrnoException e) {
640             Log.e(TAG, "Error creating UDP socket", e);
641             return false;
642         } finally {
643             TrafficStats.setThreadStatsTag(oldTag);
644         }
645         return true;
646     }
647 
connectUdpSock(Inet4Address to)648     private boolean connectUdpSock(Inet4Address to) {
649         try {
650             Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
651             return true;
652         } catch (SocketException | ErrnoException e) {
653             Log.e(TAG, "Error connecting UDP socket", e);
654             return false;
655         }
656     }
657 
getOptionsToSkip()658     private byte[] getOptionsToSkip() {
659         final ByteArrayOutputStream optionsToSkip = new ByteArrayOutputStream(2);
660         if (!isCapportApiEnabled()) optionsToSkip.write(DHCP_CAPTIVE_PORTAL);
661         if (!mConfiguration.isWifiManagedProfile) {
662             optionsToSkip.write(DHCP_DOMAIN_SEARCHLIST);
663         }
664         return optionsToSkip.toByteArray();
665     }
666 
667     private class DhcpPacketHandler extends PacketReader {
668         private FileDescriptor mPacketSock;
669 
DhcpPacketHandler(Handler handler)670         DhcpPacketHandler(Handler handler) {
671             super(handler);
672         }
673 
674         @Override
handlePacket(byte[] recvbuf, int length)675         protected void handlePacket(byte[] recvbuf, int length) {
676             try {
677                 final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf, length,
678                         DhcpPacket.ENCAP_L2, getOptionsToSkip());
679                 if (DBG) Log.d(TAG, "Received packet: " + packet);
680                 sendMessage(CMD_RECEIVED_PACKET, packet);
681             } catch (DhcpPacket.ParseException e) {
682                 Log.e(TAG, "Can't parse packet: " + e.getMessage());
683                 if (PACKET_DBG) {
684                     Log.d(TAG, HexDump.dumpHexString(recvbuf, 0, length));
685                 }
686                 if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
687                     final int snetTagId = 0x534e4554;
688                     final String bugId = "31850211";
689                     final int uid = -1;
690                     final String data = DhcpPacket.ParseException.class.getName();
691                     EventLog.writeEvent(snetTagId, bugId, uid, data);
692                 }
693                 mMetricsLog.log(mIfaceName, new DhcpErrorEvent(e.errorCode));
694                 mMetrics.addDhcpErrorCode(e.errorCode);
695             }
696         }
697 
698         @Override
createFd()699         protected FileDescriptor createFd() {
700             try {
701                 mPacketSock = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0 /* protocol */);
702                 NetworkStackUtils.attachDhcpFilter(mPacketSock);
703                 final SocketAddress addr = makePacketSocketAddress(ETH_P_IP, mIface.index);
704                 Os.bind(mPacketSock, addr);
705             } catch (SocketException | ErrnoException e) {
706                 logError("Error creating packet socket", e);
707                 if (e instanceof ErrnoException
708                         && ((ErrnoException) e).errno == 524 /* ENOTSUPP */) {
709                     Log.wtf(TAG, "Errno: ENOTSUPP");
710                 }
711                 closeFd(mPacketSock);
712                 mPacketSock = null;
713                 return null;
714             }
715             return mPacketSock;
716         }
717 
718         @Override
readPacket(FileDescriptor fd, byte[] packetBuffer)719         protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
720             try {
721                 return Os.read(fd, packetBuffer, 0, packetBuffer.length);
722             } catch (IOException | ErrnoException e) {
723                 mMetricsLog.log(mIfaceName, new DhcpErrorEvent(DhcpErrorEvent.RECEIVE_ERROR));
724                 throw e;
725             }
726         }
727 
728         @Override
logError(@onNull String msg, @Nullable Exception e)729         protected void logError(@NonNull String msg, @Nullable Exception e) {
730             Log.e(TAG, msg, e);
731         }
732 
transmitPacket(final ByteBuffer buf, final SocketAddress socketAddress)733         public int transmitPacket(final ByteBuffer buf, final SocketAddress socketAddress)
734                 throws ErrnoException, SocketException {
735             return Os.sendto(mPacketSock, buf.array(), 0 /* byteOffset */,
736                     buf.limit() /* byteCount */, 0 /* flags */, socketAddress);
737         }
738     }
739 
getSecs()740     private short getSecs() {
741         return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
742     }
743 
transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to)744     private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
745         try {
746             if (encap == DhcpPacket.ENCAP_L2) {
747                 if (DBG) Log.d(TAG, "Broadcasting " + description);
748                 mDhcpPacketHandler.transmitPacket(buf, mInterfaceBroadcastAddr);
749             } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
750                 if (DBG) Log.d(TAG, "Broadcasting " + description);
751                 // We only send L3-encapped broadcasts in DhcpRebindingState,
752                 // where we have an IP address and an unconnected UDP socket.
753                 //
754                 // N.B.: We only need this codepath because DhcpRequestPacket
755                 // hardcodes the source IP address to 0.0.0.0. We could reuse
756                 // the packet socket if this ever changes.
757                 Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
758             } else {
759                 // It's safe to call getpeername here, because we only send unicast packets if we
760                 // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
761                 if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
762                         description, Os.getpeername(mUdpSock)));
763                 Os.write(mUdpSock, buf);
764             }
765         } catch (ErrnoException | IOException e) {
766             Log.e(TAG, "Can't send packet: ", e);
767             return false;
768         }
769         return true;
770     }
771 
sendDiscoverPacket()772     private boolean sendDiscoverPacket() {
773         // When Rapid Commit option is enabled, limit only the first 3 DHCPDISCOVER packets
774         // taking Rapid Commit option, in order to prevent the potential interoperability issue
775         // and be able to rollback later. See {@link DHCP_TIMEOUT_MS} for the (re)transmission
776         // schedule with 10% jitter.
777         final boolean requestRapidCommit = isDhcpRapidCommitEnabled() && (getSecs() <= 4);
778         final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
779                 DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
780                 DO_UNICAST, getRequestedParams(), requestRapidCommit, maybeGetHostnameForSending(),
781                 mConfiguration.options);
782         mMetrics.incrementCountForDiscover();
783         return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
784     }
785 
sendRequestPacket( final Inet4Address clientAddress, final Inet4Address requestedAddress, final Inet4Address serverAddress, final Inet4Address to)786     private boolean sendRequestPacket(
787             final Inet4Address clientAddress, final Inet4Address requestedAddress,
788             final Inet4Address serverAddress, final Inet4Address to) {
789         // TODO: should we use the transaction ID from the server?
790         final int encap = INADDR_ANY.equals(clientAddress)
791                 ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
792 
793         final ByteBuffer packet = DhcpPacket.buildRequestPacket(
794                 encap, mTransactionId, getSecs(), clientAddress, DO_UNICAST, mHwAddr,
795                 requestedAddress, serverAddress, getRequestedParams(), maybeGetHostnameForSending(),
796                 mConfiguration.options);
797         String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
798         String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
799                              " request=" + requestedAddress.getHostAddress() +
800                              " serverid=" + serverStr;
801         mMetrics.incrementCountForRequest();
802         return transmitPacket(packet, description, encap, to);
803     }
804 
sendDeclinePacket(final Inet4Address requestedAddress, final Inet4Address serverIdentifier)805     private boolean sendDeclinePacket(final Inet4Address requestedAddress,
806             final Inet4Address serverIdentifier) {
807         // Requested IP address and Server Identifier options are mandatory for DHCPDECLINE.
808         final ByteBuffer packet = DhcpPacket.buildDeclinePacket(DhcpPacket.ENCAP_L2,
809                 mTransactionId, mHwAddr, requestedAddress, serverIdentifier);
810         return transmitPacket(packet, "DHCPDECLINE", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
811     }
812 
scheduleLeaseTimers()813     private void scheduleLeaseTimers() {
814         if (mDhcpLeaseExpiry == 0) {
815             Log.d(TAG, "Infinite lease, no timer scheduling needed");
816             return;
817         }
818 
819         final long now = SystemClock.elapsedRealtime();
820 
821         // TODO: consider getting the renew and rebind timers from T1 and T2.
822         // See also:
823         //     https://tools.ietf.org/html/rfc2131#section-4.4.5
824         //     https://tools.ietf.org/html/rfc1533#section-9.9
825         //     https://tools.ietf.org/html/rfc1533#section-9.10
826         final long remainingDelay = mDhcpLeaseExpiry - now;
827         final long renewDelay = remainingDelay / 2;
828         final long rebindDelay = remainingDelay * 7 / 8;
829         mT2 = now + rebindDelay;
830         mRenewAlarm.schedule(now + renewDelay);
831         mRebindAlarm.schedule(now + rebindDelay);
832         mExpiryAlarm.schedule(now + remainingDelay);
833         Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
834         Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
835         Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
836     }
837 
setLeaseExpiredToIpMemoryStore()838     private void setLeaseExpiredToIpMemoryStore() {
839         final String l2Key = mConfiguration.l2Key;
840         if (l2Key == null) return;
841         final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
842         // TODO: clear out the address and lease instead of storing an expired lease
843         na.setAssignedV4AddressExpiry(EXPIRED_LEASE);
844 
845         final OnStatusListener listener = status -> {
846             if (!status.isSuccess()) Log.e(TAG, "Failed to set lease expiry, status: " + status);
847         };
848         mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
849     }
850 
maybeSaveLeaseToIpMemoryStore()851     private void maybeSaveLeaseToIpMemoryStore() {
852         final String l2Key = mConfiguration.l2Key;
853         if (l2Key == null || mDhcpLease == null || mDhcpLease.ipAddress == null) return;
854         final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
855         na.setAssignedV4Address((Inet4Address) mDhcpLease.ipAddress.getAddress());
856         na.setAssignedV4AddressExpiry((mDhcpLease.leaseDuration == INFINITE_LEASE)
857                 ? Long.MAX_VALUE
858                 : mDhcpLease.leaseDuration * 1000 + System.currentTimeMillis());
859         na.setDnsAddresses(mDhcpLease.dnsServers);
860         na.setMtu(mDhcpLease.mtu);
861 
862         final OnStatusListener listener = status -> {
863             if (!status.isSuccess()) Log.e(TAG, "Failed to store network attrs, status: " + status);
864         };
865         mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
866     }
867 
notifySuccess()868     private void notifySuccess() {
869         maybeSaveLeaseToIpMemoryStore();
870         mController.sendMessage(
871                 CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
872     }
873 
notifyFailure(int arg)874     private void notifyFailure(int arg) {
875         setLeaseExpiredToIpMemoryStore();
876         mController.sendMessage(CMD_POST_DHCP_ACTION, arg, 0, null);
877     }
878 
acceptDhcpResults(DhcpResults results, String msg)879     private void acceptDhcpResults(DhcpResults results, String msg) {
880         mDhcpLease = results;
881         if (mDhcpLease.dnsServers.isEmpty()) {
882             // supplement customized dns servers
883             final String[] dnsServersList =
884                     mContext.getResources().getStringArray(R.array.config_default_dns_servers);
885             for (final String dnsServer : dnsServersList) {
886                 try {
887                     mDhcpLease.dnsServers.add(InetAddresses.parseNumericAddress(dnsServer));
888                 } catch (IllegalArgumentException e) {
889                     Log.e(TAG, "Invalid default DNS server: " + dnsServer, e);
890                 }
891             }
892         }
893         mOffer = null;
894         Log.d(TAG, msg + " lease: " + mDhcpLease);
895     }
896 
clearDhcpState()897     private void clearDhcpState() {
898         mDhcpLease = null;
899         mDhcpLeaseExpiry = 0;
900         mT2 = 0;
901         mOffer = null;
902     }
903 
904     /**
905      * Quit the DhcpStateMachine.
906      *
907      * @hide
908      */
doQuit()909     public void doQuit() {
910         Log.d(TAG, "doQuit");
911         quit();
912     }
913 
914     @Override
onQuitting()915     protected void onQuitting() {
916         Log.d(TAG, "onQuitting");
917         mController.sendMessage(CMD_ON_QUIT);
918     }
919 
920     abstract class LoggingState extends State {
921         private long mEnterTimeMs;
922 
923         @Override
enter()924         public void enter() {
925             if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
926             mEnterTimeMs = SystemClock.elapsedRealtime();
927         }
928 
929         @Override
exit()930         public void exit() {
931             long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
932             logState(getName(), (int) durationMs);
933         }
934 
messageName(int what)935         private String messageName(int what) {
936             return sMessageNames.get(what, Integer.toString(what));
937         }
938 
messageToString(Message message)939         private String messageToString(Message message) {
940             long now = SystemClock.uptimeMillis();
941             return new StringBuilder(" ")
942                     .append(message.getWhen() - now)
943                     .append(messageName(message.what))
944                     .append(" ").append(message.arg1)
945                     .append(" ").append(message.arg2)
946                     .append(" ").append(message.obj)
947                     .toString();
948         }
949 
950         @Override
processMessage(Message message)951         public boolean processMessage(Message message) {
952             if (MSG_DBG) {
953                 Log.d(TAG, getName() + messageToString(message));
954             }
955             return NOT_HANDLED;
956         }
957 
958         @Override
getName()959         public String getName() {
960             // All DhcpClient's states are inner classes with a well defined name.
961             // Use getSimpleName() and avoid super's getName() creating new String instances.
962             return getClass().getSimpleName();
963         }
964     }
965 
966     // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
967     // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
968     abstract class WaitBeforeOtherState extends LoggingState {
969         private final State mOtherState;
970 
WaitBeforeOtherState(State otherState)971         WaitBeforeOtherState(State otherState) {
972             mOtherState = otherState;
973         }
974 
975         @Override
enter()976         public void enter() {
977             super.enter();
978             mController.sendMessage(CMD_PRE_DHCP_ACTION);
979         }
980 
981         @Override
processMessage(Message message)982         public boolean processMessage(Message message) {
983             super.processMessage(message);
984             switch (message.what) {
985                 case CMD_PRE_DHCP_ACTION_COMPLETE:
986                     transitionTo(mOtherState);
987                     return HANDLED;
988                 default:
989                     return NOT_HANDLED;
990             }
991         }
992     }
993 
994     /**
995      * Helper method to transition to the appropriate state according to whether the pre dhcp
996      * action (e.g. turn off power optimization while doing DHCP) is required to execute.
997      * waitStateForPreDhcpAction is used to wait the pre dhcp action completed before moving to
998      * other state. If the pre dhcp action is unnecessary, transition to the target state directly.
999      */
preDhcpTransitionTo(final State waitStateForPreDhcpAction, final State targetState)1000     private void preDhcpTransitionTo(final State waitStateForPreDhcpAction,
1001             final State targetState) {
1002         transitionTo(mRegisteredForPreDhcpNotification ? waitStateForPreDhcpAction : targetState);
1003     }
1004 
1005     /**
1006      * This class is used to convey initial configuration to DhcpClient when starting DHCP.
1007      */
1008     public static class Configuration {
1009         // This is part of the initial configuration because it is passed in on startup and
1010         // never updated.
1011         // TODO: decide what to do about L2 key changes while the client is connected.
1012         @Nullable
1013         public final String l2Key;
1014         public final boolean isPreconnectionEnabled;
1015         @NonNull
1016         public final List<DhcpOption> options;
1017         public final boolean isWifiManagedProfile;
1018         public final int hostnameSetting;
1019         public final boolean populateLinkAddressLifetime;
1020 
Configuration(@ullable final String l2Key, final boolean isPreconnectionEnabled, @NonNull final List<DhcpOption> options, final boolean isWifiManagedProfile, final int hostnameSetting, final boolean populateLinkAddressLifetime)1021         public Configuration(@Nullable final String l2Key, final boolean isPreconnectionEnabled,
1022                 @NonNull final List<DhcpOption> options,
1023                 final boolean isWifiManagedProfile,
1024                 final int hostnameSetting,
1025                 final boolean populateLinkAddressLifetime) {
1026             this.l2Key = l2Key;
1027             this.isPreconnectionEnabled = isPreconnectionEnabled;
1028             this.options = options;
1029             this.isWifiManagedProfile = isWifiManagedProfile;
1030             this.hostnameSetting = hostnameSetting;
1031             this.populateLinkAddressLifetime = populateLinkAddressLifetime;
1032         }
1033     }
1034 
1035     class StoppedState extends State {
1036         @Override
processMessage(Message message)1037         public boolean processMessage(Message message) {
1038             switch (message.what) {
1039                 case CMD_START_DHCP:
1040                     mConfiguration = (Configuration) message.obj;
1041                     if (mConfiguration.isPreconnectionEnabled) {
1042                         transitionTo(mDhcpPreconnectingState);
1043                     } else {
1044                         startInitReboot();
1045                     }
1046                     recordMetricEnabledFeatures();
1047                     return HANDLED;
1048                 default:
1049                     return NOT_HANDLED;
1050             }
1051         }
1052     }
1053 
1054     class WaitBeforeStartState extends WaitBeforeOtherState {
WaitBeforeStartState(State otherState)1055         WaitBeforeStartState(State otherState) {
1056             super(otherState);
1057         }
1058     }
1059 
1060     class WaitBeforeRenewalState extends WaitBeforeOtherState {
WaitBeforeRenewalState(State otherState)1061         WaitBeforeRenewalState(State otherState) {
1062             super(otherState);
1063         }
1064     }
1065 
1066     class WaitBeforeObtainingConfigurationState extends WaitBeforeOtherState {
WaitBeforeObtainingConfigurationState(State otherState)1067         WaitBeforeObtainingConfigurationState(State otherState) {
1068             super(otherState);
1069         }
1070     }
1071 
1072     class DhcpState extends State {
1073         @Override
enter()1074         public void enter() {
1075             clearDhcpState();
1076             mConflictCount = 0;
1077             if (initInterface() && initUdpSocket()) {
1078                 mDhcpPacketHandler = new DhcpPacketHandler(getHandler());
1079                 if (mDhcpPacketHandler.start()) return;
1080                 Log.e(TAG, "Fail to start DHCP Packet Handler");
1081             }
1082             notifyFailure(DHCP_FAILURE);
1083             // We cannot call transitionTo because a transition is still in progress.
1084             // Instead, ensure that we process CMD_STOP_DHCP as soon as the transition is complete.
1085             deferMessage(obtainMessage(CMD_STOP_DHCP));
1086         }
1087 
1088         @Override
exit()1089         public void exit() {
1090             if (mDhcpPacketHandler != null) {
1091                 mDhcpPacketHandler.stop();
1092                 if (DBG) Log.d(TAG, "DHCP Packet Handler stopped");
1093             }
1094             closeSocketQuietly(mUdpSock);
1095             clearDhcpState();
1096         }
1097 
1098         @Override
processMessage(Message message)1099         public boolean processMessage(Message message) {
1100             super.processMessage(message);
1101             switch (message.what) {
1102                 case CMD_STOP_DHCP:
1103                     transitionTo(mStoppedState);
1104                     return HANDLED;
1105                 default:
1106                     return NOT_HANDLED;
1107             }
1108         }
1109     }
1110 
isValidPacket(DhcpPacket packet)1111     public boolean isValidPacket(DhcpPacket packet) {
1112         // TODO: check checksum.
1113         int xid = packet.getTransactionId();
1114         if (xid != mTransactionId) {
1115             Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId);
1116             return false;
1117         }
1118         if (!Arrays.equals(packet.getClientMac(), mHwAddr)) {
1119             Log.d(TAG, "MAC addr mismatch: got " +
1120                     HexDump.toHexString(packet.getClientMac()) + ", expected " +
1121                     HexDump.toHexString(packet.getClientMac()));
1122             return false;
1123         }
1124         return true;
1125     }
1126 
setDhcpLeaseExpiry(DhcpPacket packet)1127     public void setDhcpLeaseExpiry(DhcpPacket packet) {
1128         final int defaultMinimumLease =
1129                 mDependencies.getIntDeviceConfig(CONFIG_MINIMUM_LEASE, DEFAULT_MINIMUM_LEASE);
1130         long leaseTimeMillis = packet.getLeaseTimeMillis(defaultMinimumLease);
1131         mDhcpLeaseExpiry =
1132                 (leaseTimeMillis > 0) ? SystemClock.elapsedRealtime() + leaseTimeMillis : 0;
1133     }
1134 
1135     abstract class TimeoutState extends LoggingState {
1136         protected long mTimeout = 0;
1137 
1138         @Override
enter()1139         public void enter() {
1140             super.enter();
1141             maybeInitTimeout();
1142         }
1143 
1144         @Override
processMessage(Message message)1145         public boolean processMessage(Message message) {
1146             super.processMessage(message);
1147             switch (message.what) {
1148                 case CMD_TIMEOUT:
1149                     timeout();
1150                     return HANDLED;
1151                 default:
1152                     return NOT_HANDLED;
1153             }
1154         }
1155 
1156         @Override
exit()1157         public void exit() {
1158             super.exit();
1159             mTimeoutAlarm.cancel();
1160         }
1161 
timeout()1162         protected abstract void timeout();
maybeInitTimeout()1163         private void maybeInitTimeout() {
1164             if (mTimeout > 0) {
1165                 long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
1166                 mTimeoutAlarm.schedule(alarmTime);
1167             }
1168         }
1169     }
1170 
1171     /**
1172      * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
1173      * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
1174      * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
1175      * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
1176      * state.
1177      *
1178      * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
1179      * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
1180      * sent by the receive thread. They may also set mTimeout and implement timeout.
1181      */
1182     abstract class PacketRetransmittingState extends TimeoutState {
1183         private int mTimer;
1184 
1185         @Override
enter()1186         public void enter() {
1187             super.enter();
1188             initTimer();
1189             sendMessage(CMD_KICK);
1190         }
1191 
1192         @Override
processMessage(Message message)1193         public boolean processMessage(Message message) {
1194             if (super.processMessage(message) == HANDLED) {
1195                 return HANDLED;
1196             }
1197 
1198             switch (message.what) {
1199                 case CMD_KICK:
1200                     sendPacket();
1201                     scheduleKick();
1202                     return HANDLED;
1203                 case CMD_RECEIVED_PACKET:
1204                     receivePacket((DhcpPacket) message.obj);
1205                     return HANDLED;
1206                 default:
1207                     return NOT_HANDLED;
1208             }
1209         }
1210 
1211         @Override
exit()1212         public void exit() {
1213             super.exit();
1214             mKickAlarm.cancel();
1215         }
1216 
sendPacket()1217         protected abstract boolean sendPacket();
receivePacket(DhcpPacket packet)1218         protected abstract void receivePacket(DhcpPacket packet);
timeout()1219         protected void timeout() {}
1220 
initTimer()1221         protected void initTimer() {
1222             mTimer = FIRST_TIMEOUT_MS;
1223         }
1224 
jitterTimer(int baseTimer)1225         protected int jitterTimer(int baseTimer) {
1226             int maxJitter = baseTimer / 10;
1227             int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter;
1228             return baseTimer + jitter;
1229         }
1230 
scheduleFastKick()1231         protected void scheduleFastKick() {
1232             long now = SystemClock.elapsedRealtime();
1233             long timeout = jitterTimer(mTimer);
1234             long alarmTime = now + timeout;
1235             mKickAlarm.schedule(alarmTime);
1236             mTimer *= 2;
1237             if (mTimer > MAX_TIMEOUT_MS) {
1238                 mTimer = MAX_TIMEOUT_MS;
1239             }
1240         }
1241 
scheduleKick()1242         protected void scheduleKick() {
1243             // Always adopt the fast kick schedule by default unless this method is overrided
1244             // by subclasses.
1245             scheduleFastKick();
1246         }
1247     }
1248 
1249     class ObtainingConfigurationState extends LoggingState {
1250         @Override
enter()1251         public void enter() {
1252             super.enter();
1253 
1254             // Set a timeout for retrieving network attributes operation
1255             sendMessageDelayed(EVENT_CONFIGURATION_TIMEOUT, IPMEMORYSTORE_TIMEOUT_MS);
1256 
1257             final OnNetworkAttributesRetrievedListener listener = (status, l2Key, attributes) -> {
1258                 if (null == attributes || null == attributes.assignedV4Address) {
1259                     if (!status.isSuccess()) {
1260                         Log.e(TAG, "Error retrieving network attributes: " + status);
1261                     }
1262                     sendMessage(EVENT_CONFIGURATION_INVALID);
1263                     return;
1264                 }
1265                 sendMessage(EVENT_CONFIGURATION_OBTAINED, attributes);
1266             };
1267             mIpMemoryStore.retrieveNetworkAttributes(mConfiguration.l2Key, listener);
1268         }
1269 
1270         @Override
processMessage(Message message)1271         public boolean processMessage(Message message) {
1272             super.processMessage(message);
1273             switch (message.what) {
1274                 case EVENT_CONFIGURATION_INVALID:
1275                 case EVENT_CONFIGURATION_TIMEOUT:
1276                     transitionTo(mDhcpInitState);
1277                     return HANDLED;
1278 
1279                 case EVENT_CONFIGURATION_OBTAINED:
1280                     final long currentTime = System.currentTimeMillis();
1281                     NetworkAttributes attributes = (NetworkAttributes) message.obj;
1282                     if (DBG) {
1283                         Log.d(TAG, "l2key: "         + mConfiguration.l2Key
1284                                 + " lease address: " + attributes.assignedV4Address
1285                                 + " lease expiry: "  + attributes.assignedV4AddressExpiry
1286                                 + " current time: "  + currentTime);
1287                     }
1288                     if (currentTime >= attributes.assignedV4AddressExpiry) {
1289                         // Lease has expired.
1290                         transitionTo(mDhcpInitState);
1291                         return HANDLED;
1292                     }
1293                     mLastAssignedIpv4Address = attributes.assignedV4Address;
1294                     mLastAssignedIpv4AddressExpiry = attributes.assignedV4AddressExpiry;
1295                     transitionTo(mDhcpInitRebootState);
1296                     return HANDLED;
1297 
1298                 default:
1299                     deferMessage(message);
1300                     return HANDLED;
1301             }
1302         }
1303 
1304         @Override
exit()1305         public void exit() {
1306             super.exit();
1307             removeMessages(EVENT_CONFIGURATION_INVALID);
1308             removeMessages(EVENT_CONFIGURATION_TIMEOUT);
1309             removeMessages(EVENT_CONFIGURATION_OBTAINED);
1310         }
1311     }
1312 
maybeTransitionToIpv6OnlyWaitState(@onNull final DhcpPacket packet)1313     private boolean maybeTransitionToIpv6OnlyWaitState(@NonNull final DhcpPacket packet) {
1314         if (packet.getIpv6OnlyWaitTimeMillis() == DhcpPacket.V6ONLY_PREFERRED_ABSENCE) return false;
1315 
1316         mIpv6OnlyWaitTimeMs = packet.getIpv6OnlyWaitTimeMillis();
1317         transitionTo(mIpv6OnlyWaitState);
1318         return true;
1319     }
1320 
receiveOfferOrAckPacket(final DhcpPacket packet, final boolean acceptRapidCommit)1321     private void receiveOfferOrAckPacket(final DhcpPacket packet, final boolean acceptRapidCommit) {
1322         if (!isValidPacket(packet)) return;
1323 
1324         // 1. received the DHCPOFFER packet, process it by following RFC2131.
1325         // 2. received the DHCPACK packet from DHCP Servers that support Rapid
1326         //    Commit option, process it by following RFC4039.
1327         if (packet instanceof DhcpOfferPacket) {
1328             if (maybeTransitionToIpv6OnlyWaitState(packet)) {
1329                 return;
1330             }
1331             mOffer = packet.toDhcpResults();
1332             if (mOffer != null) {
1333                 Log.d(TAG, "Got pending lease: " + mOffer);
1334                 transitionTo(mDhcpRequestingState);
1335             }
1336         } else if (packet instanceof DhcpAckPacket) {
1337             // If rapid commit is not enabled in DhcpInitState, or enablePreconnection is
1338             // not enabled in DhcpPreconnectingState, ignore DHCPACK packet. Only DHCPACK
1339             // with the rapid commit option are valid.
1340             if (!acceptRapidCommit || !packet.mRapidCommit) return;
1341 
1342             final DhcpResults results = packet.toDhcpResults();
1343             if (results != null) {
1344                 confirmDhcpLease(packet, results);
1345                 transitionTo(isDhcpIpConflictDetectEnabled()
1346                         ? mIpAddressConflictDetectingState : mConfiguringInterfaceState);
1347             }
1348         }
1349     }
1350 
1351     class DhcpInitState extends PacketRetransmittingState {
DhcpInitState()1352         public DhcpInitState() {
1353             super();
1354         }
1355 
1356         @Override
enter()1357         public void enter() {
1358             super.enter();
1359             startNewTransaction();
1360             mLastInitEnterTime = SystemClock.elapsedRealtime();
1361         }
1362 
sendPacket()1363         protected boolean sendPacket() {
1364             return sendDiscoverPacket();
1365         }
1366 
receivePacket(DhcpPacket packet)1367         protected void receivePacket(DhcpPacket packet) {
1368             receiveOfferOrAckPacket(packet, isDhcpRapidCommitEnabled());
1369         }
1370     }
1371 
startInitReboot()1372     private void startInitReboot() {
1373         preDhcpTransitionTo(mWaitBeforeObtainingConfigurationState, mObtainingConfigurationState);
1374     }
1375 
1376     class DhcpPreconnectingState extends TimeoutState {
1377         // This state is used to support Fast Initial Link Setup (FILS) IP Address Setup
1378         // procedure defined in the IEEE802.11ai (2016) currently. However, this state could
1379         // be extended to support other intended useage as well in the future, e.g. pre-actions
1380         // should be completed in advance before the normal DHCP solicit process starts.
DhcpPreconnectingState()1381         DhcpPreconnectingState() {
1382             mTimeout = FIRST_TIMEOUT_MS;
1383         }
1384 
1385         @Override
enter()1386         public void enter() {
1387             super.enter();
1388             startNewTransaction();
1389             mLastInitEnterTime = SystemClock.elapsedRealtime();
1390             sendPreconnectionPacket();
1391         }
1392 
1393         @Override
processMessage(Message message)1394         public boolean processMessage(Message message) {
1395             if (super.processMessage(message) == HANDLED) {
1396                 return HANDLED;
1397             }
1398 
1399             switch (message.what) {
1400                 case CMD_RECEIVED_PACKET:
1401                     receiveOfferOrAckPacket((DhcpPacket) message.obj,
1402                             mConfiguration.isPreconnectionEnabled);
1403                     return HANDLED;
1404                 case CMD_ABORT_PRECONNECTION:
1405                     startInitReboot();
1406                     return HANDLED;
1407                 default:
1408                     return NOT_HANDLED;
1409             }
1410         }
1411 
1412         // This timeout is necessary and cannot just be replaced with a notification that
1413         // preconnection is complete. This is because:
1414         // - The preconnection complete notification could arrive before the ACK with rapid
1415         //   commit arrives. In this case we would go back to init state, pick a new transaction
1416         //   ID, and when the ACK with rapid commit arrives, we would ignore it because the
1417         //   transaction ID doesn't match.
1418         // - We cannot just wait in this state until the ACK with rapid commit arrives, because
1419         //   if that ACK never arrives (e.g., dropped by the network), we'll never go back to init
1420         //   and send a DISCOVER.
1421         @Override
timeout()1422         public void timeout() {
1423             startInitReboot();
1424         }
1425 
sendPreconnectionPacket()1426         private void sendPreconnectionPacket() {
1427             final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable();
1428             final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
1429                     DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
1430                     DO_UNICAST, getRequestedParams(), true /* rapid commit */,
1431                     maybeGetHostnameForSending(),
1432                     mConfiguration.options);
1433 
1434             l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST);
1435             l2Packet.payload = Arrays.copyOf(packet.array(), packet.limit());
1436             mController.sendMessage(CMD_START_PRECONNECTION, l2Packet);
1437         }
1438     }
1439 
1440     // Not implemented. We request the first offer we receive.
1441     class DhcpSelectingState extends LoggingState {
1442     }
1443 
1444     class DhcpRequestingState extends PacketRetransmittingState {
DhcpRequestingState()1445         public DhcpRequestingState() {
1446             mTimeout = DHCP_TIMEOUT_MS / 2;
1447         }
1448 
sendPacket()1449         protected boolean sendPacket() {
1450             return sendRequestPacket(
1451                     INADDR_ANY,                                    // ciaddr
1452                     (Inet4Address) mOffer.ipAddress.getAddress(),  // DHCP_REQUESTED_IP
1453                     (Inet4Address) mOffer.serverAddress,           // DHCP_SERVER_IDENTIFIER
1454                     INADDR_BROADCAST);                             // packet destination address
1455         }
1456 
receivePacket(DhcpPacket packet)1457         protected void receivePacket(DhcpPacket packet) {
1458             if (!isValidPacket(packet)) return;
1459             if ((packet instanceof DhcpAckPacket)) {
1460                 if (maybeTransitionToIpv6OnlyWaitState(packet)) {
1461                     return;
1462                 }
1463                 final DhcpResults results = packet.toDhcpResults();
1464                 if (results != null) {
1465                     confirmDhcpLease(packet, results);
1466                     transitionTo(isDhcpIpConflictDetectEnabled()
1467                             ? mIpAddressConflictDetectingState : mConfiguringInterfaceState);
1468                 }
1469             } else if (packet instanceof DhcpNakPacket) {
1470                 // TODO: Wait a while before returning into INIT state.
1471                 Log.d(TAG, "Received NAK, returning to INIT");
1472                 mOffer = null;
1473                 transitionTo(mDhcpInitState);
1474             }
1475         }
1476 
1477         @Override
timeout()1478         protected void timeout() {
1479             // After sending REQUESTs unsuccessfully for a while, go back to init.
1480             transitionTo(mDhcpInitState);
1481         }
1482     }
1483 
1484     class DhcpHaveLeaseState extends State {
1485         @Override
processMessage(Message message)1486         public boolean processMessage(Message message) {
1487             switch (message.what) {
1488                 case CMD_EXPIRE_DHCP:
1489                     Log.d(TAG, "Lease expired!");
1490                     notifyFailure(DHCP_FAILURE);
1491                     transitionTo(mStoppedState);
1492                     return HANDLED;
1493                 default:
1494                     return NOT_HANDLED;
1495             }
1496         }
1497 
1498         @Override
exit()1499         public void exit() {
1500             // Clear any extant alarms.
1501             mRenewAlarm.cancel();
1502             mRebindAlarm.cancel();
1503             mExpiryAlarm.cancel();
1504             clearDhcpState();
1505             // Tell IpManager to clear the IPv4 address. There is no need to
1506             // wait for confirmation since any subsequent packets are sent from
1507             // INADDR_ANY anyway (DISCOVER, REQUEST).
1508             mController.sendMessage(CMD_CLEAR_LINKADDRESS);
1509         }
1510     }
1511 
1512     class ConfiguringInterfaceState extends LoggingState {
1513         @Override
enter()1514         public void enter() {
1515             super.enter();
1516             // We must call notifySuccess to apply the rest of the DHCP configuration (e.g., DNS
1517             // servers) before adding the IP address to the interface. Otherwise, as soon as
1518             // IpClient sees the IP address appear, it will enter provisioned state without any
1519             // configuration information from DHCP. http://b/146850745.
1520             notifySuccess();
1521             mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.leaseDuration, 0,
1522                     mDhcpLease.ipAddress);
1523         }
1524 
1525         @Override
processMessage(Message message)1526         public boolean processMessage(Message message) {
1527             super.processMessage(message);
1528             switch (message.what) {
1529                 case EVENT_LINKADDRESS_CONFIGURED:
1530                     transitionTo(mDhcpBoundState);
1531                     return HANDLED;
1532                 default:
1533                     return NOT_HANDLED;
1534             }
1535         }
1536     }
1537 
1538     private class IpConflictDetector extends PacketReader {
1539         private FileDescriptor mArpSock;
1540         private final Inet4Address mTargetIp;
1541 
IpConflictDetector(@onNull Handler handler, @NonNull Inet4Address ipAddress)1542         IpConflictDetector(@NonNull Handler handler, @NonNull Inet4Address ipAddress) {
1543             super(handler);
1544             mTargetIp = ipAddress;
1545         }
1546 
1547         @Override
handlePacket(byte[] recvbuf, int length)1548         protected void handlePacket(byte[] recvbuf, int length) {
1549             try {
1550                 final ArpPacket packet = ArpPacket.parseArpPacket(recvbuf, length);
1551                 if (hasIpAddressConflict(packet, mTargetIp)) {
1552                     mMetrics.incrementCountForIpConflict();
1553                     sendMessage(EVENT_IP_CONFLICT);
1554                 }
1555             } catch (ArpPacket.ParseException e) {
1556                 logError("Can't parse ARP packet", e);
1557             }
1558         }
1559 
1560         @Override
createFd()1561         protected FileDescriptor createFd() {
1562             try {
1563                 mArpSock = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0 /* protocol */);
1564                 SocketAddress addr = makePacketSocketAddress(ETH_P_ARP, mIface.index);
1565                 Os.bind(mArpSock, addr);
1566                 return mArpSock;
1567             } catch (SocketException | ErrnoException e) {
1568                 logError("Error creating ARP socket", e);
1569                 closeFd(mArpSock);
1570                 mArpSock = null;
1571                 return null;
1572             }
1573         }
1574 
1575         @Override
logError(@onNull String msg, @NonNull Exception e)1576         protected void logError(@NonNull String msg, @NonNull Exception e) {
1577             Log.e(TAG, msg, e);
1578         }
1579 
transmitPacket(@onNull Inet4Address targetIp, @NonNull Inet4Address senderIp, final byte[] hwAddr, @NonNull SocketAddress sockAddr)1580         public boolean transmitPacket(@NonNull Inet4Address targetIp,
1581                 @NonNull Inet4Address senderIp, final byte[] hwAddr,
1582                 @NonNull SocketAddress sockAddr) {
1583             // RFC5227 3. describes both ARP Probes and Announcements use ARP Request packet.
1584             final ByteBuffer packet = ArpPacket.buildArpPacket(DhcpPacket.ETHER_BROADCAST, hwAddr,
1585                     targetIp.getAddress(), new byte[ETHER_ADDR_LEN], senderIp.getAddress(),
1586                     (short) ARP_REQUEST);
1587             try {
1588                 Os.sendto(mArpSock, packet.array(), 0 /* byteOffset */,
1589                         packet.limit() /* byteCount */, 0 /* flags */, sockAddr);
1590                 return true;
1591             } catch (ErrnoException | SocketException e) {
1592                 logError("Can't send ARP packet", e);
1593                 return false;
1594             }
1595         }
1596     }
1597 
isArpProbe(@onNull ArpPacket packet)1598     private boolean isArpProbe(@NonNull ArpPacket packet) {
1599         return (packet.opCode == ARP_REQUEST && packet.senderIp.equals(INADDR_ANY)
1600                 && !packet.targetIp.equals(INADDR_ANY));
1601     }
1602 
1603     // RFC5227 2.1.1 says, during probing period:
1604     // 1. the host receives any ARP packet (Request *or* Reply) on the interface where the
1605     //    probe is being performed, where the packet's 'sender IP address' is the address
1606     //    being probed for, then the host MUST treat this address as conflict.
1607     // 2. the host receives any ARP Probe where the packet's 'target IP address' is the
1608     //    address being probed for, and the packet's 'sender hardware address' is not the
1609     //    hardware address of any of the host's interfaces, then the host SHOULD similarly
1610     //    treat this as an address conflict.
packetHasIpAddressConflict(@onNull ArpPacket packet, @NonNull Inet4Address targetIp)1611     private boolean packetHasIpAddressConflict(@NonNull ArpPacket packet,
1612             @NonNull Inet4Address targetIp) {
1613         return (((!packet.senderIp.equals(INADDR_ANY) && packet.senderIp.equals(targetIp))
1614                 || (isArpProbe(packet) && packet.targetIp.equals(targetIp)))
1615                 && !Arrays.equals(packet.senderHwAddress.toByteArray(), mHwAddr));
1616     }
1617 
hasIpAddressConflict(@onNull ArpPacket packet, @NonNull Inet4Address targetIp)1618     private boolean hasIpAddressConflict(@NonNull ArpPacket packet,
1619             @NonNull Inet4Address targetIp) {
1620         if (!packetHasIpAddressConflict(packet, targetIp)) return false;
1621         if (DBG) {
1622             final String senderIpString = packet.senderIp.getHostAddress();
1623             final String targetIpString = packet.targetIp.getHostAddress();
1624             final MacAddress senderMacAddress = packet.senderHwAddress;
1625             final MacAddress hostMacAddress = MacAddress.fromBytes(mHwAddr);
1626             Log.d(TAG, "IP address conflict detected:"
1627                     + (packet.opCode == ARP_REQUEST ? "ARP Request" : "ARP Reply")
1628                     + " ARP sender MAC: " + senderMacAddress.toString()
1629                     + " host MAC: "       + hostMacAddress.toString()
1630                     + " ARP sender IP: "  + senderIpString
1631                     + " ARP target IP: "  + targetIpString
1632                     + " host target IP: " + targetIp.getHostAddress());
1633         }
1634         return true;
1635     }
1636 
1637     class IpAddressConflictDetectingState extends LoggingState {
1638         private int mArpProbeCount;
1639         private int mArpAnnounceCount;
1640         private Inet4Address mTargetIp;
1641         private IpConflictDetector mIpConflictDetector;
1642         private PowerManager.WakeLock mTimeoutWakeLock;
1643 
1644         private int mArpFirstProbeDelayMs;
1645         private int mArpProbeMaxDelayMs;
1646         private int mArpProbeMinDelayMs;
1647         private int mArpFirstAnnounceDelayMs;
1648         private int mArpAnnounceIntervalMs;
1649 
1650         @Override
enter()1651         public void enter() {
1652             super.enter();
1653 
1654             mArpProbeCount = 0;
1655             mArpAnnounceCount = 0;
1656 
1657             // IP address conflict detection occurs after receiving DHCPACK
1658             // message every time, i.e. we already get an available lease from
1659             // DHCP server, that ensures mDhcpLease should be NonNull, see
1660             // {@link DhcpRequestingState#receivePacket} for details.
1661             mTargetIp = (Inet4Address) mDhcpLease.ipAddress.getAddress();
1662             mIpConflictDetector = new IpConflictDetector(getHandler(), mTargetIp);
1663 
1664             // IpConflictDetector might fail to create the raw socket.
1665             if (!mIpConflictDetector.start()) {
1666                 Log.e(TAG, "Fail to start IP Conflict Detector");
1667                 transitionTo(mConfiguringInterfaceState);
1668                 return;
1669             }
1670 
1671             // Read the customized parameters from DeviceConfig.
1672             readIpConflictParametersFromDeviceConfig();
1673             if (VDBG) {
1674                 Log.d(TAG, "ARP First Probe delay: "    + mArpFirstProbeDelayMs
1675                         + " ARP Probe Max delay: "      + mArpProbeMaxDelayMs
1676                         + " ARP Probe Min delay: "      + mArpProbeMinDelayMs
1677                         + " ARP First Announce delay: " + mArpFirstAnnounceDelayMs
1678                         + " ARP Announce interval: "    + mArpAnnounceIntervalMs);
1679             }
1680 
1681             // Note that when we get here, we're still processing the WakeupMessage that caused
1682             // us to transition into this state, and thus the AlarmManager is still holding its
1683             // wakelock. That wakelock might expire as soon as this method returns.
1684             final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
1685             mTimeoutWakeLock = mDependencies.getWakeLock(powerManager);
1686             mTimeoutWakeLock.acquire();
1687 
1688             // RFC5227 2.1.1 describes waiting for a random time interval between 0 and
1689             // PROBE_WAIT seconds before sending probe packets PROBE_NUM times, this delay
1690             // helps avoid hosts send initial probe packet simultaneously upon power on.
1691             // Probe packet transmission interval spaces randomly and uniformly between
1692             // PROBE_MIN and PROBE_MAX.
1693             sendMessageDelayed(CMD_ARP_PROBE, mRandom.nextInt(mArpFirstProbeDelayMs));
1694         }
1695 
1696         @Override
processMessage(Message message)1697         public boolean processMessage(Message message) {
1698             super.processMessage(message);
1699             switch (message.what) {
1700                 case CMD_ARP_PROBE:
1701                     // According to RFC5227, wait ANNOUNCE_WAIT seconds after
1702                     // the last ARP Probe, and no conflicting ARP Reply or ARP
1703                     // Probe has been received within this period, then host can
1704                     // determine the desired IP address may be used safely.
1705                     sendArpProbe();
1706                     if (++mArpProbeCount < IPV4_CONFLICT_PROBE_NUM) {
1707                         scheduleProbe();
1708                     } else {
1709                         scheduleAnnounce(mArpFirstAnnounceDelayMs);
1710                     }
1711                     return HANDLED;
1712                 case CMD_ARP_ANNOUNCEMENT:
1713                     sendArpAnnounce();
1714                     if (++mArpAnnounceCount < IPV4_CONFLICT_ANNOUNCE_NUM) {
1715                         scheduleAnnounce(mArpAnnounceIntervalMs);
1716                     } else {
1717                         transitionTo(mConfiguringInterfaceState);
1718                     }
1719                     return HANDLED;
1720                 case EVENT_IP_CONFLICT:
1721                     transitionTo(mDhcpDecliningState);
1722                     return HANDLED;
1723                 default:
1724                     return NOT_HANDLED;
1725             }
1726         }
1727 
1728         // Because the timing parameters used in IP Address detection mechanism are in
1729         // milliseconds, WakeupMessage would be too imprecise for small timeouts.
scheduleProbe()1730         private void scheduleProbe() {
1731             long timeout = mRandom.nextInt(mArpProbeMaxDelayMs - mArpProbeMinDelayMs)
1732                     + mArpProbeMinDelayMs;
1733             sendMessageDelayed(CMD_ARP_PROBE, timeout);
1734         }
1735 
scheduleAnnounce(final int timeout)1736         private void scheduleAnnounce(final int timeout) {
1737             sendMessageDelayed(CMD_ARP_ANNOUNCEMENT, timeout);
1738         }
1739 
1740         @Override
exit()1741         public void exit() {
1742             super.exit();
1743             mTimeoutWakeLock.release();
1744             mIpConflictDetector.stop();
1745             if (DBG) Log.d(TAG, "IP Conflict Detector stopped");
1746             removeMessages(CMD_ARP_PROBE);
1747             removeMessages(CMD_ARP_ANNOUNCEMENT);
1748             removeMessages(EVENT_IP_CONFLICT);
1749         }
1750 
1751         // The following timing parameters are defined in RFC5227 as fixed constants, which
1752         // are too long to adopt in the mobile network scenario, however more appropriate to
1753         // reference these fixed value as maximumValue argument to restrict the upper bound,
1754         // the minimum values of 10/20ms are used to avoid tight loops due to misconfiguration.
readIpConflictParametersFromDeviceConfig()1755         private void readIpConflictParametersFromDeviceConfig() {
1756             // PROBE_WAIT
1757             mArpFirstProbeDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_PROBE_DELAY_MS,
1758                     10, MAX_ARP_FIRST_PROBE_DELAY_MS, DEFAULT_ARP_FIRST_PROBE_DELAY_MS);
1759 
1760             // PROBE_MIN
1761             mArpProbeMinDelayMs = mDependencies.getIntDeviceConfig(ARP_PROBE_MIN_MS, 10,
1762                     MAX_ARP_PROBE_MIN_MS, DEFAULT_ARP_PROBE_MIN_MS);
1763 
1764             // PROBE_MAX
1765             mArpProbeMaxDelayMs = Math.max(mArpProbeMinDelayMs + 1,
1766                     mDependencies.getIntDeviceConfig(ARP_PROBE_MAX_MS, 20, MAX_ARP_PROBE_MAX_MS,
1767                     DEFAULT_ARP_PROBE_MAX_MS));
1768 
1769             // ANNOUNCE_WAIT
1770             mArpFirstAnnounceDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_ANNOUNCE_DELAY_MS,
1771                     20, MAX_ARP_FIRST_ANNOUNCE_DELAY_MS, DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS);
1772 
1773             // ANNOUNCE_INTERVAL
1774             mArpAnnounceIntervalMs = mDependencies.getIntDeviceConfig(ARP_ANNOUNCE_INTERVAL_MS, 20,
1775                     MAX_ARP_ANNOUNCE_INTERVAL_MS, DEFAULT_ARP_ANNOUNCE_INTERVAL_MS);
1776         }
1777 
sendArpProbe()1778         private boolean sendArpProbe() {
1779             return mIpConflictDetector.transmitPacket(mTargetIp /* target IP */,
1780                     INADDR_ANY /* sender IP */, mHwAddr, mInterfaceBroadcastAddr);
1781         }
1782 
sendArpAnnounce()1783         private boolean sendArpAnnounce() {
1784             return mIpConflictDetector.transmitPacket(mTargetIp /* target IP */,
1785                     mTargetIp /* sender IP */, mHwAddr, mInterfaceBroadcastAddr);
1786         }
1787     }
1788 
1789     class DhcpBoundState extends LoggingState {
1790         @Override
enter()1791         public void enter() {
1792             super.enter();
1793             if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
1794                 // There's likely no point in going into DhcpInitState here, we'll probably
1795                 // just repeat the transaction, get the same IP address as before, and fail.
1796                 //
1797                 // NOTE: It is observed that connectUdpSock() basically never fails, due to
1798                 // SO_BINDTODEVICE. Examining the local socket address shows it will happily
1799                 // return an IPv4 address from another interface, or even return "0.0.0.0".
1800                 //
1801                 // TODO: Consider deleting this check, following testing on several kernels.
1802                 notifyFailure(DHCP_FAILURE);
1803                 transitionTo(mStoppedState);
1804             }
1805 
1806             scheduleLeaseTimers();
1807             logTimeToBoundState();
1808         }
1809 
1810         @Override
exit()1811         public void exit() {
1812             super.exit();
1813             mLastBoundExitTime = SystemClock.elapsedRealtime();
1814         }
1815 
1816         @Override
processMessage(Message message)1817         public boolean processMessage(Message message) {
1818             super.processMessage(message);
1819             switch (message.what) {
1820                 case CMD_RENEW_DHCP:
1821                     preDhcpTransitionTo(mWaitBeforeRenewalState, mDhcpRenewingState);
1822                     return HANDLED;
1823                 case CMD_REFRESH_LINKADDRESS:
1824                     transitionTo(mDhcpRefreshingAddressState);
1825                     return HANDLED;
1826                 default:
1827                     return NOT_HANDLED;
1828             }
1829         }
1830 
logTimeToBoundState()1831         private void logTimeToBoundState() {
1832             long now = SystemClock.elapsedRealtime();
1833             if (mLastBoundExitTime > mLastInitEnterTime) {
1834                 logState(EVENT_RENEWING_BOUND, (int) (now - mLastBoundExitTime));
1835             } else {
1836                 logState(EVENT_INITIAL_BOUND, (int) (now - mLastInitEnterTime));
1837             }
1838         }
1839     }
1840 
1841     abstract class DhcpReacquiringState extends PacketRetransmittingState {
1842         protected String mLeaseMsg;
1843 
1844         @Override
enter()1845         public void enter() {
1846             super.enter();
1847             startNewTransaction();
1848         }
1849 
packetDestination()1850         protected abstract Inet4Address packetDestination();
1851 
1852         // Check whether DhcpClient should notify provisioning failure when receiving DHCPNAK
1853         // in renew/rebind state or just restart reconfiguration from StoppedState.
shouldRestartOnNak()1854         protected abstract boolean shouldRestartOnNak();
1855 
1856         // Schedule alarm for the next DHCPREQUEST tranmission. Per RFC2131 if the client
1857         // receives no response to its DHCPREQUEST message, the client should wait one-half
1858         // of the remaining time until T2 in RENEWING state, and one-half of the remaining
1859         // lease time in REBINDING state, down to a minimum of 60 seconds before transmitting
1860         // DHCPREQUEST.
1861         private static final long MIN_DELAY_BEFORE_NEXT_REQUEST = 60_000L;
scheduleSlowKick(final long target)1862         protected void scheduleSlowKick(final long target) {
1863             final long now = SystemClock.elapsedRealtime();
1864             long remainingDelay = (target - now) / 2;
1865             if (remainingDelay < MIN_DELAY_BEFORE_NEXT_REQUEST) {
1866                 remainingDelay = MIN_DELAY_BEFORE_NEXT_REQUEST;
1867             }
1868             mKickAlarm.schedule(now + remainingDelay);
1869         }
1870 
sendPacket()1871         protected boolean sendPacket() {
1872             return sendRequestPacket(
1873                     (Inet4Address) mDhcpLease.ipAddress.getAddress(),  // ciaddr
1874                     INADDR_ANY,                                        // DHCP_REQUESTED_IP
1875                     null,                                              // DHCP_SERVER_IDENTIFIER
1876                     packetDestination());                              // packet destination address
1877         }
1878 
receivePacket(DhcpPacket packet)1879         protected void receivePacket(DhcpPacket packet) {
1880             if (!isValidPacket(packet)) return;
1881             if ((packet instanceof DhcpAckPacket)) {
1882                 if (maybeTransitionToIpv6OnlyWaitState(packet)) {
1883                     return;
1884                 }
1885                 final DhcpResults results = packet.toDhcpResults();
1886                 if (results != null) {
1887                     if (!mDhcpLease.ipAddress.equals(results.ipAddress)) {
1888                         Log.d(TAG, "Renewed lease not for our current IP address!");
1889                         notifyFailure(DHCP_FAILURE);
1890                         transitionTo(mStoppedState);
1891                         return;
1892                     }
1893                     setDhcpLeaseExpiry(packet);
1894                     // Updating our notion of DhcpResults here only causes the
1895                     // DNS servers and routes to be updated in LinkProperties
1896                     // in IpManager and by any overridden relevant handlers of
1897                     // the registered IpManager.Callback.  IP address changes
1898                     // are not supported here.
1899                     acceptDhcpResults(results, mLeaseMsg);
1900                     if (mConfiguration.populateLinkAddressLifetime) {
1901                         // Transit to ConfiguringInterfaceState and notify address renew
1902                         // or rebind with success, and refresh the IPv4 address lifetime
1903                         // via netlink message there. Otherwise, the IPv4 address will end
1904                         // up being deleted from the interface when the address lifetime
1905                         // expires. Transit back to BoundState later and schedule new lease
1906                         // expiry once the address lifetime is successfully updated.
1907                         // This change is required since the user space updates the deprecationTime
1908                         // and expirationTime of IPv4 link address when it receives the netlink
1909                         // message from kernel. Previously the lifetime of an IPv4 address was
1910                         // always permanent, so we don't need to maintain lifetime updates in
1911                         // user space.
1912                         transitionTo(mConfiguringInterfaceState);
1913                     } else {
1914                         notifySuccess();
1915                         transitionTo(mDhcpBoundState);
1916                     }
1917                 }
1918             } else if (packet instanceof DhcpNakPacket) {
1919                 Log.d(TAG, "Received NAK, returning to StoppedState");
1920                 notifyFailure(shouldRestartOnNak() ? DHCP_REFRESH_FAILURE : DHCP_FAILURE);
1921                 transitionTo(mStoppedState);
1922             }
1923         }
1924     }
1925 
1926     class DhcpRenewingState extends DhcpReacquiringState {
DhcpRenewingState()1927         public DhcpRenewingState() {
1928             mLeaseMsg = "Renewed";
1929         }
1930 
1931         @Override
processMessage(Message message)1932         public boolean processMessage(Message message) {
1933             if (super.processMessage(message) == HANDLED) {
1934                 return HANDLED;
1935             }
1936 
1937             switch (message.what) {
1938                 case CMD_REBIND_DHCP:
1939                     transitionTo(mDhcpRebindingState);
1940                     return HANDLED;
1941                 default:
1942                     return NOT_HANDLED;
1943             }
1944         }
1945 
1946         @Override
packetDestination()1947         protected Inet4Address packetDestination() {
1948             // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
1949             // http://b/25343517 . Try to make things work anyway by using broadcast renews.
1950             return (mDhcpLease.serverAddress != null) ?
1951                     mDhcpLease.serverAddress : INADDR_BROADCAST;
1952         }
1953 
1954         @Override
shouldRestartOnNak()1955         protected boolean shouldRestartOnNak() {
1956             return false;
1957         }
1958 
1959         @Override
scheduleKick()1960         protected void scheduleKick() {
1961             if (isSlowRetransmissionEnabled()) {
1962                 scheduleSlowKick(mT2);
1963             } else {
1964                 scheduleFastKick();
1965             }
1966         }
1967     }
1968 
1969     class DhcpRebindingBaseState extends DhcpReacquiringState {
1970         @Override
enter()1971         public void enter() {
1972             super.enter();
1973 
1974             // We need to broadcast and possibly reconnect the socket to a
1975             // completely different server.
1976             closeSocketQuietly(mUdpSock);
1977             if (!initUdpSocket()) {
1978                 Log.e(TAG, "Failed to recreate UDP socket");
1979                 transitionTo(mDhcpInitState);
1980             }
1981         }
1982 
1983         @Override
packetDestination()1984         protected Inet4Address packetDestination() {
1985             return INADDR_BROADCAST;
1986         }
1987 
1988         @Override
shouldRestartOnNak()1989         protected boolean shouldRestartOnNak() {
1990             return false;
1991         }
1992     }
1993 
1994     class DhcpRebindingState extends DhcpRebindingBaseState {
DhcpRebindingState()1995         DhcpRebindingState() {
1996             mLeaseMsg = "Rebound";
1997         }
1998 
1999         @Override
scheduleKick()2000         protected void scheduleKick() {
2001             if (isSlowRetransmissionEnabled()) {
2002                 scheduleSlowKick(mDhcpLeaseExpiry);
2003             } else {
2004                 scheduleFastKick();
2005             }
2006         }
2007     }
2008 
2009     // The slow retransmission approach complied with RFC2131 should only be applied
2010     // for Renewing and Rebinding state. For this state it's expected to refresh IPv4
2011     // link address after roam as soon as possible, obviously it should not adopt the
2012     // slow retransmission algorithm. Create a base DhcpRebindingBaseState state and
2013     // have both of DhcpRebindingState and DhcpRefreshingAddressState extend from it,
2014     // then override the scheduleKick method in DhcpRebindingState to comply with slow
2015     // schedule and keep DhcpRefreshingAddressState as-is to use the fast schedule.
2016     class DhcpRefreshingAddressState extends DhcpRebindingBaseState {
DhcpRefreshingAddressState()2017         DhcpRefreshingAddressState() {
2018             mLeaseMsg = "Refreshing address";
2019         }
2020 
2021         @Override
shouldRestartOnNak()2022         protected boolean shouldRestartOnNak() {
2023             return true;
2024         }
2025     }
2026 
2027     class DhcpInitRebootState extends DhcpRequestingState {
2028         @Override
enter()2029         public void enter() {
2030             mTimeout = DHCP_INITREBOOT_TIMEOUT_MS;
2031             super.enter();
2032             startNewTransaction();
2033         }
2034 
2035         // RFC 2131 4.3.2 describes generated DHCPREQUEST message during
2036         // INIT-REBOOT state:
2037         // 'server identifier' MUST NOT be filled in, 'requested IP address'
2038         // option MUST be filled in with client's notion of its previously
2039         // assigned address. 'ciaddr' MUST be zero. The client is seeking to
2040         // verify a previously allocated, cached configuration. Server SHOULD
2041         // send a DHCPNAK message to the client if the 'requested IP address'
2042         // is incorrect, or is on the wrong network.
2043         @Override
sendPacket()2044         protected boolean sendPacket() {
2045             return sendRequestPacket(
2046                     INADDR_ANY,                                        // ciaddr
2047                     mLastAssignedIpv4Address,                          // DHCP_REQUESTED_IP
2048                     null,                                              // DHCP_SERVER_IDENTIFIER
2049                     INADDR_BROADCAST);                                 // packet destination address
2050         }
2051 
2052         @Override
exit()2053         public void exit() {
2054             mLastAssignedIpv4Address = null;
2055             mLastAssignedIpv4AddressExpiry = 0;
2056         }
2057     }
2058 
2059     class DhcpRebootingState extends LoggingState {
2060     }
2061 
2062     class DhcpDecliningState extends TimeoutState {
2063         @Override
enter()2064         public void enter() {
2065             // If the host experiences MAX_CONFLICTS or more address conflicts on the
2066             // interface, configure interface with this IP address anyway.
2067             if (++mConflictCount > MAX_CONFLICTS_COUNT) {
2068                 transitionTo(mConfiguringInterfaceState);
2069                 return;
2070             }
2071 
2072             mTimeout = mDependencies.getIntDeviceConfig(DHCP_RESTART_CONFIG_DELAY, 100,
2073                     MAX_DHCP_CLIENT_RESTART_CONFIG_DELAY_MS, DEFAULT_DHCP_RESTART_CONFIG_DELAY_MS);
2074             super.enter();
2075             sendPacket();
2076         }
2077 
2078         // No need to override processMessage here since this state is
2079         // functionally identical to its superclass TimeoutState.
timeout()2080         protected void timeout() {
2081             transitionTo(mDhcpInitState);
2082         }
2083 
sendPacket()2084         private boolean sendPacket() {
2085             return sendDeclinePacket(
2086                     (Inet4Address) mDhcpLease.ipAddress.getAddress(),  // requested IP
2087                     (Inet4Address) mDhcpLease.serverAddress);          // serverIdentifier
2088         }
2089     }
2090 
2091     // This state is used for IPv6-only preferred mode defined in the draft-ietf-dhc-v6only.
2092     // For IPv6-only capable host, it will forgo obtaining an IPv4 address for V6ONLY_WAIT
2093     // period if the network indicates that it can provide IPv6 connectivity by replying
2094     // with a valid IPv6-only preferred option in the DHCPOFFER or DHCPACK.
2095     class Ipv6OnlyWaitState extends TimeoutState {
2096         @Override
enter()2097         public void enter() {
2098             mTimeout = mIpv6OnlyWaitTimeMs;
2099             super.enter();
2100 
2101             // Restore power save and suspend optimization if it was disabled before.
2102             if (mRegisteredForPreDhcpNotification) {
2103                 mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_IPV6_ONLY, 0, null);
2104             }
2105         }
2106 
2107         @Override
exit()2108         public void exit() {
2109             mIpv6OnlyWaitTimeMs = 0;
2110         }
2111 
timeout()2112         protected void timeout() {
2113             startInitReboot();
2114         }
2115     }
2116 
logState(String name, int durationMs)2117     private void logState(String name, int durationMs) {
2118         final DhcpClientEvent event = new DhcpClientEvent.Builder()
2119                 .setMsg(name)
2120                 .setDurationMs(durationMs)
2121                 .build();
2122         mMetricsLog.log(mIfaceName, event);
2123     }
2124 }
2125