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