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