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