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