1 /* 2 * Copyright (C) 2016 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.ip; 18 19 import com.android.internal.util.MessageUtils; 20 import com.android.internal.util.WakeupMessage; 21 22 import android.content.Context; 23 import android.net.apf.ApfCapabilities; 24 import android.net.apf.ApfFilter; 25 import android.net.DhcpResults; 26 import android.net.InterfaceConfiguration; 27 import android.net.LinkAddress; 28 import android.net.LinkProperties; 29 import android.net.LinkProperties.ProvisioningChange; 30 import android.net.ProxyInfo; 31 import android.net.RouteInfo; 32 import android.net.StaticIpConfiguration; 33 import android.net.dhcp.DhcpClient; 34 import android.net.metrics.IpConnectivityLog; 35 import android.net.metrics.IpManagerEvent; 36 import android.net.util.AvoidBadWifiTracker; 37 import android.os.INetworkManagementService; 38 import android.os.Message; 39 import android.os.RemoteException; 40 import android.os.ServiceManager; 41 import android.os.SystemClock; 42 import android.text.TextUtils; 43 import android.util.LocalLog; 44 import android.util.Log; 45 import android.util.SparseArray; 46 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.util.IndentingPrintWriter; 49 import com.android.internal.util.State; 50 import com.android.internal.util.StateMachine; 51 import com.android.server.net.NetlinkTracker; 52 53 import java.io.FileDescriptor; 54 import java.io.PrintWriter; 55 import java.net.InetAddress; 56 import java.net.NetworkInterface; 57 import java.net.SocketException; 58 import java.util.Objects; 59 import java.util.StringJoiner; 60 61 62 /** 63 * IpManager 64 * 65 * This class provides the interface to IP-layer provisioning and maintenance 66 * functionality that can be used by transport layers like Wi-Fi, Ethernet, 67 * et cetera. 68 * 69 * [ Lifetime ] 70 * IpManager is designed to be instantiated as soon as the interface name is 71 * known and can be as long-lived as the class containing it (i.e. declaring 72 * it "private final" is okay). 73 * 74 * @hide 75 */ 76 public class IpManager extends StateMachine { 77 private static final boolean DBG = false; 78 private static final boolean VDBG = false; 79 80 // For message logging. 81 private static final Class[] sMessageClasses = { IpManager.class, DhcpClient.class }; 82 private static final SparseArray<String> sWhatToString = 83 MessageUtils.findMessageNames(sMessageClasses); 84 85 /** 86 * Callbacks for handling IpManager events. 87 */ 88 public static class Callback { 89 // In order to receive onPreDhcpAction(), call #withPreDhcpAction() 90 // when constructing a ProvisioningConfiguration. 91 // 92 // Implementations of onPreDhcpAction() must call 93 // IpManager#completedPreDhcpAction() to indicate that DHCP is clear 94 // to proceed. onPreDhcpAction()95 public void onPreDhcpAction() {} onPostDhcpAction()96 public void onPostDhcpAction() {} 97 98 // This is purely advisory and not an indication of provisioning 99 // success or failure. This is only here for callers that want to 100 // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress). 101 // DHCPv4 or static IPv4 configuration failure or success can be 102 // determined by whether or not the passed-in DhcpResults object is 103 // null or not. onNewDhcpResults(DhcpResults dhcpResults)104 public void onNewDhcpResults(DhcpResults dhcpResults) {} 105 onProvisioningSuccess(LinkProperties newLp)106 public void onProvisioningSuccess(LinkProperties newLp) {} onProvisioningFailure(LinkProperties newLp)107 public void onProvisioningFailure(LinkProperties newLp) {} 108 109 // Invoked on LinkProperties changes. onLinkPropertiesChange(LinkProperties newLp)110 public void onLinkPropertiesChange(LinkProperties newLp) {} 111 112 // Called when the internal IpReachabilityMonitor (if enabled) has 113 // detected the loss of a critical number of required neighbors. onReachabilityLost(String logMsg)114 public void onReachabilityLost(String logMsg) {} 115 116 // Called when the IpManager state machine terminates. onQuit()117 public void onQuit() {} 118 119 // Install an APF program to filter incoming packets. installPacketFilter(byte[] filter)120 public void installPacketFilter(byte[] filter) {} 121 122 // If multicast filtering cannot be accomplished with APF, this function will be called to 123 // actuate multicast filtering using another means. setFallbackMulticastFilter(boolean enabled)124 public void setFallbackMulticastFilter(boolean enabled) {} 125 126 // Enabled/disable Neighbor Discover offload functionality. This is 127 // called, for example, whenever 464xlat is being started or stopped. setNeighborDiscoveryOffload(boolean enable)128 public void setNeighborDiscoveryOffload(boolean enable) {} 129 } 130 131 public static class WaitForProvisioningCallback extends Callback { 132 private LinkProperties mCallbackLinkProperties; 133 waitForProvisioning()134 public LinkProperties waitForProvisioning() { 135 synchronized (this) { 136 try { 137 wait(); 138 } catch (InterruptedException e) {} 139 return mCallbackLinkProperties; 140 } 141 } 142 143 @Override onProvisioningSuccess(LinkProperties newLp)144 public void onProvisioningSuccess(LinkProperties newLp) { 145 synchronized (this) { 146 mCallbackLinkProperties = newLp; 147 notify(); 148 } 149 } 150 151 @Override onProvisioningFailure(LinkProperties newLp)152 public void onProvisioningFailure(LinkProperties newLp) { 153 synchronized (this) { 154 mCallbackLinkProperties = null; 155 notify(); 156 } 157 } 158 } 159 160 // Use a wrapper class to log in order to ensure complete and detailed 161 // logging. This method is lighter weight than annotations/reflection 162 // and has the following benefits: 163 // 164 // - No invoked method can be forgotten. 165 // Any new method added to IpManager.Callback must be overridden 166 // here or it will never be called. 167 // 168 // - No invoking call site can be forgotten. 169 // Centralized logging in this way means call sites don't need to 170 // remember to log, and therefore no call site can be forgotten. 171 // 172 // - No variation in log format among call sites. 173 // Encourages logging of any available arguments, and all call sites 174 // are necessarily logged identically. 175 // 176 // TODO: Find an lighter weight approach. 177 private class LoggingCallbackWrapper extends Callback { 178 private static final String PREFIX = "INVOKE "; 179 private Callback mCallback; 180 LoggingCallbackWrapper(Callback callback)181 public LoggingCallbackWrapper(Callback callback) { 182 mCallback = callback; 183 } 184 log(String msg)185 private void log(String msg) { 186 mLocalLog.log(PREFIX + msg); 187 } 188 189 @Override onPreDhcpAction()190 public void onPreDhcpAction() { 191 mCallback.onPreDhcpAction(); 192 log("onPreDhcpAction()"); 193 } 194 @Override onPostDhcpAction()195 public void onPostDhcpAction() { 196 mCallback.onPostDhcpAction(); 197 log("onPostDhcpAction()"); 198 } 199 @Override onNewDhcpResults(DhcpResults dhcpResults)200 public void onNewDhcpResults(DhcpResults dhcpResults) { 201 mCallback.onNewDhcpResults(dhcpResults); 202 log("onNewDhcpResults({" + dhcpResults + "})"); 203 } 204 @Override onProvisioningSuccess(LinkProperties newLp)205 public void onProvisioningSuccess(LinkProperties newLp) { 206 mCallback.onProvisioningSuccess(newLp); 207 log("onProvisioningSuccess({" + newLp + "})"); 208 } 209 @Override onProvisioningFailure(LinkProperties newLp)210 public void onProvisioningFailure(LinkProperties newLp) { 211 mCallback.onProvisioningFailure(newLp); 212 log("onProvisioningFailure({" + newLp + "})"); 213 } 214 @Override onLinkPropertiesChange(LinkProperties newLp)215 public void onLinkPropertiesChange(LinkProperties newLp) { 216 mCallback.onLinkPropertiesChange(newLp); 217 log("onLinkPropertiesChange({" + newLp + "})"); 218 } 219 @Override onReachabilityLost(String logMsg)220 public void onReachabilityLost(String logMsg) { 221 mCallback.onReachabilityLost(logMsg); 222 log("onReachabilityLost(" + logMsg + ")"); 223 } 224 @Override onQuit()225 public void onQuit() { 226 mCallback.onQuit(); 227 log("onQuit()"); 228 } 229 @Override installPacketFilter(byte[] filter)230 public void installPacketFilter(byte[] filter) { 231 mCallback.installPacketFilter(filter); 232 log("installPacketFilter(byte[" + filter.length + "])"); 233 } 234 @Override setFallbackMulticastFilter(boolean enabled)235 public void setFallbackMulticastFilter(boolean enabled) { 236 mCallback.setFallbackMulticastFilter(enabled); 237 log("setFallbackMulticastFilter(" + enabled + ")"); 238 } 239 @Override setNeighborDiscoveryOffload(boolean enable)240 public void setNeighborDiscoveryOffload(boolean enable) { 241 mCallback.setNeighborDiscoveryOffload(enable); 242 log("setNeighborDiscoveryOffload(" + enable + ")"); 243 } 244 } 245 246 /** 247 * This class encapsulates parameters to be passed to 248 * IpManager#startProvisioning(). A defensive copy is made by IpManager 249 * and the values specified herein are in force until IpManager#stop() 250 * is called. 251 * 252 * Example use: 253 * 254 * final ProvisioningConfiguration config = 255 * mIpManager.buildProvisioningConfiguration() 256 * .withPreDhcpAction() 257 * .withProvisioningTimeoutMs(36 * 1000) 258 * .build(); 259 * mIpManager.startProvisioning(config); 260 * ... 261 * mIpManager.stop(); 262 * 263 * The specified provisioning configuration will only be active until 264 * IpManager#stop() is called. Future calls to IpManager#startProvisioning() 265 * must specify the configuration again. 266 */ 267 public static class ProvisioningConfiguration { 268 // TODO: Delete this default timeout once those callers that care are 269 // fixed to pass in their preferred timeout. 270 // 271 // We pick 36 seconds so we can send DHCP requests at 272 // 273 // t=0, t=2, t=6, t=14, t=30 274 // 275 // allowing for 10% jitter. 276 private static final int DEFAULT_TIMEOUT_MS = 36 * 1000; 277 278 public static class Builder { 279 private ProvisioningConfiguration mConfig = new ProvisioningConfiguration(); 280 withoutIPv4()281 public Builder withoutIPv4() { 282 mConfig.mEnableIPv4 = false; 283 return this; 284 } 285 withoutIPv6()286 public Builder withoutIPv6() { 287 mConfig.mEnableIPv6 = false; 288 return this; 289 } 290 withoutIpReachabilityMonitor()291 public Builder withoutIpReachabilityMonitor() { 292 mConfig.mUsingIpReachabilityMonitor = false; 293 return this; 294 } 295 withPreDhcpAction()296 public Builder withPreDhcpAction() { 297 mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS; 298 return this; 299 } 300 withPreDhcpAction(int dhcpActionTimeoutMs)301 public Builder withPreDhcpAction(int dhcpActionTimeoutMs) { 302 mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs; 303 return this; 304 } 305 withStaticConfiguration(StaticIpConfiguration staticConfig)306 public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) { 307 mConfig.mStaticIpConfig = staticConfig; 308 return this; 309 } 310 withApfCapabilities(ApfCapabilities apfCapabilities)311 public Builder withApfCapabilities(ApfCapabilities apfCapabilities) { 312 mConfig.mApfCapabilities = apfCapabilities; 313 return this; 314 } 315 withProvisioningTimeoutMs(int timeoutMs)316 public Builder withProvisioningTimeoutMs(int timeoutMs) { 317 mConfig.mProvisioningTimeoutMs = timeoutMs; 318 return this; 319 } 320 build()321 public ProvisioningConfiguration build() { 322 return new ProvisioningConfiguration(mConfig); 323 } 324 } 325 326 /* package */ boolean mEnableIPv4 = true; 327 /* package */ boolean mEnableIPv6 = true; 328 /* package */ boolean mUsingIpReachabilityMonitor = true; 329 /* package */ int mRequestedPreDhcpActionMs; 330 /* package */ StaticIpConfiguration mStaticIpConfig; 331 /* package */ ApfCapabilities mApfCapabilities; 332 /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; 333 ProvisioningConfiguration()334 public ProvisioningConfiguration() {} 335 ProvisioningConfiguration(ProvisioningConfiguration other)336 public ProvisioningConfiguration(ProvisioningConfiguration other) { 337 mEnableIPv4 = other.mEnableIPv4; 338 mEnableIPv6 = other.mEnableIPv6; 339 mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor; 340 mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs; 341 mStaticIpConfig = other.mStaticIpConfig; 342 mApfCapabilities = other.mApfCapabilities; 343 mProvisioningTimeoutMs = other.mProvisioningTimeoutMs; 344 } 345 346 @Override toString()347 public String toString() { 348 return new StringJoiner(", ", getClass().getSimpleName() + "{", "}") 349 .add("mEnableIPv4: " + mEnableIPv4) 350 .add("mEnableIPv6: " + mEnableIPv6) 351 .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor) 352 .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs) 353 .add("mStaticIpConfig: " + mStaticIpConfig) 354 .add("mApfCapabilities: " + mApfCapabilities) 355 .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs) 356 .toString(); 357 } 358 } 359 360 public static final String DUMP_ARG = "ipmanager"; 361 362 private static final int CMD_STOP = 1; 363 private static final int CMD_START = 2; 364 private static final int CMD_CONFIRM = 3; 365 private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 4; 366 // Sent by NetlinkTracker to communicate netlink events. 367 private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5; 368 private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6; 369 private static final int CMD_UPDATE_HTTP_PROXY = 7; 370 private static final int CMD_SET_MULTICAST_FILTER = 8; 371 private static final int EVENT_PROVISIONING_TIMEOUT = 9; 372 private static final int EVENT_DHCPACTION_TIMEOUT = 10; 373 374 private static final int MAX_LOG_RECORDS = 500; 375 376 private static final boolean NO_CALLBACKS = false; 377 private static final boolean SEND_CALLBACKS = true; 378 379 // This must match the interface prefix in clatd.c. 380 // TODO: Revert this hack once IpManager and Nat464Xlat work in concert. 381 private static final String CLAT_PREFIX = "v4-"; 382 383 private final State mStoppedState = new StoppedState(); 384 private final State mStoppingState = new StoppingState(); 385 private final State mStartedState = new StartedState(); 386 387 private final String mTag; 388 private final Context mContext; 389 private final String mInterfaceName; 390 private final String mClatInterfaceName; 391 @VisibleForTesting 392 protected final Callback mCallback; 393 private final INetworkManagementService mNwService; 394 private final NetlinkTracker mNetlinkTracker; 395 private final WakeupMessage mProvisioningTimeoutAlarm; 396 private final WakeupMessage mDhcpActionTimeoutAlarm; 397 private final AvoidBadWifiTracker mAvoidBadWifiTracker; 398 private final LocalLog mLocalLog; 399 private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); 400 401 private NetworkInterface mNetworkInterface; 402 403 /** 404 * Non-final member variables accessed only from within our StateMachine. 405 */ 406 private LinkProperties mLinkProperties; 407 private ProvisioningConfiguration mConfiguration; 408 private IpReachabilityMonitor mIpReachabilityMonitor; 409 private DhcpClient mDhcpClient; 410 private DhcpResults mDhcpResults; 411 private String mTcpBufferSizes; 412 private ProxyInfo mHttpProxy; 413 private ApfFilter mApfFilter; 414 private boolean mMulticastFiltering; 415 private long mStartTimeMillis; 416 IpManager(Context context, String ifName, Callback callback)417 public IpManager(Context context, String ifName, Callback callback) 418 throws IllegalArgumentException { 419 this(context, ifName, callback, INetworkManagementService.Stub.asInterface( 420 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE))); 421 } 422 423 /** 424 * An expanded constructor, useful for dependency injection. 425 */ IpManager(Context context, String ifName, Callback callback, INetworkManagementService nwService)426 public IpManager(Context context, String ifName, Callback callback, 427 INetworkManagementService nwService) throws IllegalArgumentException { 428 super(IpManager.class.getSimpleName() + "." + ifName); 429 mTag = getName(); 430 431 mContext = context; 432 mInterfaceName = ifName; 433 mClatInterfaceName = CLAT_PREFIX + ifName; 434 mCallback = new LoggingCallbackWrapper(callback); 435 mNwService = nwService; 436 437 mNetlinkTracker = new NetlinkTracker( 438 mInterfaceName, 439 new NetlinkTracker.Callback() { 440 @Override 441 public void update() { 442 sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED); 443 } 444 }) { 445 @Override 446 public void interfaceAdded(String iface) { 447 super.interfaceAdded(iface); 448 if (mClatInterfaceName.equals(iface)) { 449 mCallback.setNeighborDiscoveryOffload(false); 450 } 451 } 452 453 @Override 454 public void interfaceRemoved(String iface) { 455 super.interfaceRemoved(iface); 456 if (mClatInterfaceName.equals(iface)) { 457 // TODO: consider sending a message to the IpManager main 458 // StateMachine thread, in case "NDO enabled" state becomes 459 // tied to more things that 464xlat operation. 460 mCallback.setNeighborDiscoveryOffload(true); 461 } 462 } 463 }; 464 465 try { 466 mNwService.registerObserver(mNetlinkTracker); 467 } catch (RemoteException e) { 468 Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString()); 469 } 470 471 mAvoidBadWifiTracker = new AvoidBadWifiTracker(mContext, getHandler()); 472 473 resetLinkProperties(); 474 475 mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(), 476 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT); 477 mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(), 478 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT); 479 480 // Super simple StateMachine. 481 addState(mStoppedState); 482 addState(mStartedState); 483 addState(mStoppingState); 484 485 setInitialState(mStoppedState); 486 mLocalLog = new LocalLog(MAX_LOG_RECORDS); 487 super.start(); 488 } 489 490 @Override onQuitting()491 protected void onQuitting() { 492 mCallback.onQuit(); 493 } 494 495 // Shut down this IpManager instance altogether. shutdown()496 public void shutdown() { 497 stop(); 498 quit(); 499 } 500 buildProvisioningConfiguration()501 public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() { 502 return new ProvisioningConfiguration.Builder(); 503 } 504 startProvisioning(ProvisioningConfiguration req)505 public void startProvisioning(ProvisioningConfiguration req) { 506 getNetworkInterface(); 507 508 mCallback.setNeighborDiscoveryOffload(true); 509 sendMessage(CMD_START, new ProvisioningConfiguration(req)); 510 } 511 512 // TODO: Delete this. startProvisioning(StaticIpConfiguration staticIpConfig)513 public void startProvisioning(StaticIpConfiguration staticIpConfig) { 514 startProvisioning(buildProvisioningConfiguration() 515 .withStaticConfiguration(staticIpConfig) 516 .build()); 517 } 518 startProvisioning()519 public void startProvisioning() { 520 startProvisioning(new ProvisioningConfiguration()); 521 } 522 stop()523 public void stop() { 524 sendMessage(CMD_STOP); 525 } 526 confirmConfiguration()527 public void confirmConfiguration() { 528 sendMessage(CMD_CONFIRM); 529 } 530 completedPreDhcpAction()531 public void completedPreDhcpAction() { 532 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 533 } 534 535 /** 536 * Set the TCP buffer sizes to use. 537 * 538 * This may be called, repeatedly, at any time before or after a call to 539 * #startProvisioning(). The setting is cleared upon calling #stop(). 540 */ setTcpBufferSizes(String tcpBufferSizes)541 public void setTcpBufferSizes(String tcpBufferSizes) { 542 sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes); 543 } 544 545 /** 546 * Set the HTTP Proxy configuration to use. 547 * 548 * This may be called, repeatedly, at any time before or after a call to 549 * #startProvisioning(). The setting is cleared upon calling #stop(). 550 */ setHttpProxy(ProxyInfo proxyInfo)551 public void setHttpProxy(ProxyInfo proxyInfo) { 552 sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo); 553 } 554 555 /** 556 * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering, 557 * if not, Callback.setFallbackMulticastFilter() is called. 558 */ setMulticastFilter(boolean enabled)559 public void setMulticastFilter(boolean enabled) { 560 sendMessage(CMD_SET_MULTICAST_FILTER, enabled); 561 } 562 dump(FileDescriptor fd, PrintWriter writer, String[] args)563 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 564 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 565 pw.println("APF dump:"); 566 pw.increaseIndent(); 567 // Thread-unsafe access to mApfFilter but just used for debugging. 568 ApfFilter apfFilter = mApfFilter; 569 if (apfFilter != null) { 570 apfFilter.dump(pw); 571 } else { 572 pw.println("No apf support"); 573 } 574 pw.decreaseIndent(); 575 576 pw.println(); 577 pw.println("StateMachine dump:"); 578 pw.increaseIndent(); 579 mLocalLog.readOnlyLocalLog().dump(fd, pw, args); 580 pw.decreaseIndent(); 581 } 582 583 584 /** 585 * Internals. 586 */ 587 588 @Override getWhatToString(int what)589 protected String getWhatToString(int what) { 590 return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what)); 591 } 592 593 @Override getLogRecString(Message msg)594 protected String getLogRecString(Message msg) { 595 final String logLine = String.format( 596 "%s/%d %d %d %s", 597 mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(), 598 msg.arg1, msg.arg2, Objects.toString(msg.obj)); 599 600 final String richerLogLine = getWhatToString(msg.what) + " " + logLine; 601 mLocalLog.log(richerLogLine); 602 if (VDBG) { 603 Log.d(mTag, richerLogLine); 604 } 605 606 return logLine; 607 } 608 609 @Override recordLogRec(Message msg)610 protected boolean recordLogRec(Message msg) { 611 // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy, 612 // and we already log any LinkProperties change that results in an 613 // invocation of IpManager.Callback#onLinkPropertiesChange(). 614 return (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED); 615 } 616 getNetworkInterface()617 private void getNetworkInterface() { 618 try { 619 mNetworkInterface = NetworkInterface.getByName(mInterfaceName); 620 } catch (SocketException | NullPointerException e) { 621 // TODO: throw new IllegalStateException. 622 Log.e(mTag, "ALERT: Failed to get interface object: ", e); 623 } 624 } 625 626 // This needs to be called with care to ensure that our LinkProperties 627 // are in sync with the actual LinkProperties of the interface. For example, 628 // we should only call this if we know for sure that there are no IP addresses 629 // assigned to the interface, etc. resetLinkProperties()630 private void resetLinkProperties() { 631 mNetlinkTracker.clearLinkProperties(); 632 mConfiguration = null; 633 mDhcpResults = null; 634 mTcpBufferSizes = ""; 635 mHttpProxy = null; 636 637 mLinkProperties = new LinkProperties(); 638 mLinkProperties.setInterfaceName(mInterfaceName); 639 } 640 recordMetric(final int type)641 private void recordMetric(final int type) { 642 if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); } 643 final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis; 644 mMetricsLog.log(new IpManagerEvent(mInterfaceName, type, duration)); 645 } 646 647 // For now: use WifiStateMachine's historical notion of provisioned. isProvisioned(LinkProperties lp)648 private static boolean isProvisioned(LinkProperties lp) { 649 // For historical reasons, we should connect even if all we have is 650 // an IPv4 address and nothing else. 651 return lp.isProvisioned() || lp.hasIPv4Address(); 652 } 653 654 // TODO: Investigate folding all this into the existing static function 655 // LinkProperties.compareProvisioning() or some other single function that 656 // takes two LinkProperties objects and returns a ProvisioningChange 657 // object that is a correct and complete assessment of what changed, taking 658 // account of the asymmetries described in the comments in this function. 659 // Then switch to using it everywhere (IpReachabilityMonitor, etc.). compareProvisioning( LinkProperties oldLp, LinkProperties newLp)660 private ProvisioningChange compareProvisioning( 661 LinkProperties oldLp, LinkProperties newLp) { 662 ProvisioningChange delta; 663 664 final boolean wasProvisioned = isProvisioned(oldLp); 665 final boolean isProvisioned = isProvisioned(newLp); 666 667 if (!wasProvisioned && isProvisioned) { 668 delta = ProvisioningChange.GAINED_PROVISIONING; 669 } else if (wasProvisioned && isProvisioned) { 670 delta = ProvisioningChange.STILL_PROVISIONED; 671 } else if (!wasProvisioned && !isProvisioned) { 672 delta = ProvisioningChange.STILL_NOT_PROVISIONED; 673 } else { 674 // (wasProvisioned && !isProvisioned) 675 // 676 // Note that this is true even if we lose a configuration element 677 // (e.g., a default gateway) that would not be required to advance 678 // into provisioned state. This is intended: if we have a default 679 // router and we lose it, that's a sure sign of a problem, but if 680 // we connect to a network with no IPv4 DNS servers, we consider 681 // that to be a network without DNS servers and connect anyway. 682 // 683 // See the comment below. 684 delta = ProvisioningChange.LOST_PROVISIONING; 685 } 686 687 final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned(); 688 final boolean lostIPv4Address = oldLp.hasIPv4Address() && !newLp.hasIPv4Address(); 689 final boolean lostIPv6Router = oldLp.hasIPv6DefaultRoute() && !newLp.hasIPv6DefaultRoute(); 690 691 // If bad wifi avoidance is disabled, then ignore IPv6 loss of 692 // provisioning. Otherwise, when a hotspot that loses Internet 693 // access sends out a 0-lifetime RA to its clients, the clients 694 // will disconnect and then reconnect, avoiding the bad hotspot, 695 // instead of getting stuck on the bad hotspot. http://b/31827713 . 696 // 697 // This is incorrect because if the hotspot then regains Internet 698 // access with a different prefix, TCP connections on the 699 // deprecated addresses will remain stuck. 700 // 701 // Note that we can still be disconnected by IpReachabilityMonitor 702 // if the IPv6 default gateway (but not the IPv6 DNS servers; see 703 // accompanying code in IpReachabilityMonitor) is unreachable. 704 final boolean ignoreIPv6ProvisioningLoss = !mAvoidBadWifiTracker.currentValue(); 705 706 // Additionally: 707 // 708 // Partial configurations (e.g., only an IPv4 address with no DNS 709 // servers and no default route) are accepted as long as DHCPv4 710 // succeeds. On such a network, isProvisioned() will always return 711 // false, because the configuration is not complete, but we want to 712 // connect anyway. It might be a disconnected network such as a 713 // Chromecast or a wireless printer, for example. 714 // 715 // Because on such a network isProvisioned() will always return false, 716 // delta will never be LOST_PROVISIONING. So check for loss of 717 // provisioning here too. 718 if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) { 719 delta = ProvisioningChange.LOST_PROVISIONING; 720 } 721 722 // Additionally: 723 // 724 // If the previous link properties had a global IPv6 address and an 725 // IPv6 default route then also consider the loss of that default route 726 // to be a loss of provisioning. See b/27962810. 727 if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) { 728 delta = ProvisioningChange.LOST_PROVISIONING; 729 } 730 731 return delta; 732 } 733 dispatchCallback(ProvisioningChange delta, LinkProperties newLp)734 private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) { 735 switch (delta) { 736 case GAINED_PROVISIONING: 737 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); } 738 recordMetric(IpManagerEvent.PROVISIONING_OK); 739 mCallback.onProvisioningSuccess(newLp); 740 break; 741 742 case LOST_PROVISIONING: 743 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } 744 recordMetric(IpManagerEvent.PROVISIONING_FAIL); 745 mCallback.onProvisioningFailure(newLp); 746 break; 747 748 default: 749 if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); } 750 mCallback.onLinkPropertiesChange(newLp); 751 break; 752 } 753 } 754 755 // Updates all IpManager-related state concerned with LinkProperties. 756 // Returns a ProvisioningChange for possibly notifying other interested 757 // parties that are not fronted by IpManager. setLinkProperties(LinkProperties newLp)758 private ProvisioningChange setLinkProperties(LinkProperties newLp) { 759 if (mApfFilter != null) { 760 mApfFilter.setLinkProperties(newLp); 761 } 762 if (mIpReachabilityMonitor != null) { 763 mIpReachabilityMonitor.updateLinkProperties(newLp); 764 } 765 766 ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp); 767 mLinkProperties = new LinkProperties(newLp); 768 769 if (delta == ProvisioningChange.GAINED_PROVISIONING) { 770 // TODO: Add a proper ProvisionedState and cancel the alarm in 771 // its enter() method. 772 mProvisioningTimeoutAlarm.cancel(); 773 } 774 775 return delta; 776 } 777 linkPropertiesUnchanged(LinkProperties newLp)778 private boolean linkPropertiesUnchanged(LinkProperties newLp) { 779 return Objects.equals(newLp, mLinkProperties); 780 } 781 assembleLinkProperties()782 private LinkProperties assembleLinkProperties() { 783 // [1] Create a new LinkProperties object to populate. 784 LinkProperties newLp = new LinkProperties(); 785 newLp.setInterfaceName(mInterfaceName); 786 787 // [2] Pull in data from netlink: 788 // - IPv4 addresses 789 // - IPv6 addresses 790 // - IPv6 routes 791 // - IPv6 DNS servers 792 LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties(); 793 newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses()); 794 for (RouteInfo route : netlinkLinkProperties.getRoutes()) { 795 newLp.addRoute(route); 796 } 797 for (InetAddress dns : netlinkLinkProperties.getDnsServers()) { 798 // Only add likely reachable DNS servers. 799 // TODO: investigate deleting this. 800 if (newLp.isReachable(dns)) { 801 newLp.addDnsServer(dns); 802 } 803 } 804 805 // [3] Add in data from DHCPv4, if available. 806 // 807 // mDhcpResults is never shared with any other owner so we don't have 808 // to worry about concurrent modification. 809 if (mDhcpResults != null) { 810 for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) { 811 newLp.addRoute(route); 812 } 813 for (InetAddress dns : mDhcpResults.dnsServers) { 814 // Only add likely reachable DNS servers. 815 // TODO: investigate deleting this. 816 if (newLp.isReachable(dns)) { 817 newLp.addDnsServer(dns); 818 } 819 } 820 newLp.setDomains(mDhcpResults.domains); 821 822 if (mDhcpResults.mtu != 0) { 823 newLp.setMtu(mDhcpResults.mtu); 824 } 825 } 826 827 // [4] Add in TCP buffer sizes and HTTP Proxy config, if available. 828 if (!TextUtils.isEmpty(mTcpBufferSizes)) { 829 newLp.setTcpBufferSizes(mTcpBufferSizes); 830 } 831 if (mHttpProxy != null) { 832 newLp.setHttpProxy(mHttpProxy); 833 } 834 835 if (VDBG) { 836 Log.d(mTag, "newLp{" + newLp + "}"); 837 } 838 return newLp; 839 } 840 841 // Returns false if we have lost provisioning, true otherwise. handleLinkPropertiesUpdate(boolean sendCallbacks)842 private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) { 843 final LinkProperties newLp = assembleLinkProperties(); 844 if (linkPropertiesUnchanged(newLp)) { 845 return true; 846 } 847 final ProvisioningChange delta = setLinkProperties(newLp); 848 if (sendCallbacks) { 849 dispatchCallback(delta, newLp); 850 } 851 return (delta != ProvisioningChange.LOST_PROVISIONING); 852 } 853 setIPv4Address(LinkAddress address)854 private boolean setIPv4Address(LinkAddress address) { 855 final InterfaceConfiguration ifcg = new InterfaceConfiguration(); 856 ifcg.setLinkAddress(address); 857 try { 858 mNwService.setInterfaceConfig(mInterfaceName, ifcg); 859 if (VDBG) Log.d(mTag, "IPv4 configuration succeeded"); 860 } catch (IllegalStateException | RemoteException e) { 861 Log.e(mTag, "IPv4 configuration failed: ", e); 862 return false; 863 } 864 return true; 865 } 866 clearIPv4Address()867 private void clearIPv4Address() { 868 try { 869 final InterfaceConfiguration ifcg = new InterfaceConfiguration(); 870 ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0")); 871 mNwService.setInterfaceConfig(mInterfaceName, ifcg); 872 } catch (IllegalStateException | RemoteException e) { 873 Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e); 874 } 875 } 876 handleIPv4Success(DhcpResults dhcpResults)877 private void handleIPv4Success(DhcpResults dhcpResults) { 878 mDhcpResults = new DhcpResults(dhcpResults); 879 final LinkProperties newLp = assembleLinkProperties(); 880 final ProvisioningChange delta = setLinkProperties(newLp); 881 882 if (VDBG) { 883 Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); 884 } 885 mCallback.onNewDhcpResults(dhcpResults); 886 dispatchCallback(delta, newLp); 887 } 888 handleIPv4Failure()889 private void handleIPv4Failure() { 890 // TODO: Investigate deleting this clearIPv4Address() call. 891 // 892 // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances 893 // that could trigger a call to this function. If we missed handling 894 // that message in StartedState for some reason we would still clear 895 // any addresses upon entry to StoppedState. 896 clearIPv4Address(); 897 mDhcpResults = null; 898 if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); } 899 mCallback.onNewDhcpResults(null); 900 901 handleProvisioningFailure(); 902 } 903 handleProvisioningFailure()904 private void handleProvisioningFailure() { 905 final LinkProperties newLp = assembleLinkProperties(); 906 ProvisioningChange delta = setLinkProperties(newLp); 907 // If we've gotten here and we're still not provisioned treat that as 908 // a total loss of provisioning. 909 // 910 // Either (a) static IP configuration failed or (b) DHCPv4 failed AND 911 // there was no usable IPv6 obtained before a non-zero provisioning 912 // timeout expired. 913 // 914 // Regardless: GAME OVER. 915 if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) { 916 delta = ProvisioningChange.LOST_PROVISIONING; 917 } 918 919 dispatchCallback(delta, newLp); 920 if (delta == ProvisioningChange.LOST_PROVISIONING) { 921 transitionTo(mStoppingState); 922 } 923 } 924 startIPv4()925 private boolean startIPv4() { 926 // If we have a StaticIpConfiguration attempt to apply it and 927 // handle the result accordingly. 928 if (mConfiguration.mStaticIpConfig != null) { 929 if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) { 930 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig)); 931 } else { 932 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } 933 recordMetric(IpManagerEvent.PROVISIONING_FAIL); 934 mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties)); 935 return false; 936 } 937 } else { 938 // Start DHCPv4. 939 mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName); 940 mDhcpClient.registerForPreDhcpNotification(); 941 mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); 942 } 943 944 return true; 945 } 946 startIPv6()947 private boolean startIPv6() { 948 // Set privacy extensions. 949 try { 950 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); 951 mNwService.enableIpv6(mInterfaceName); 952 } catch (RemoteException re) { 953 Log.e(mTag, "Unable to change interface settings: " + re); 954 return false; 955 } catch (IllegalStateException ie) { 956 Log.e(mTag, "Unable to change interface settings: " + ie); 957 return false; 958 } 959 960 return true; 961 } 962 963 964 class StoppedState extends State { 965 @Override enter()966 public void enter() { 967 try { 968 mNwService.disableIpv6(mInterfaceName); 969 mNwService.clearInterfaceAddresses(mInterfaceName); 970 } catch (Exception e) { 971 Log.e(mTag, "Failed to clear addresses or disable IPv6" + e); 972 } 973 974 resetLinkProperties(); 975 if (mStartTimeMillis > 0) { 976 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE); 977 mStartTimeMillis = 0; 978 } 979 } 980 981 @Override processMessage(Message msg)982 public boolean processMessage(Message msg) { 983 switch (msg.what) { 984 case CMD_STOP: 985 break; 986 987 case CMD_START: 988 mConfiguration = (ProvisioningConfiguration) msg.obj; 989 transitionTo(mStartedState); 990 break; 991 992 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 993 handleLinkPropertiesUpdate(NO_CALLBACKS); 994 break; 995 996 case CMD_UPDATE_TCP_BUFFER_SIZES: 997 mTcpBufferSizes = (String) msg.obj; 998 handleLinkPropertiesUpdate(NO_CALLBACKS); 999 break; 1000 1001 case CMD_UPDATE_HTTP_PROXY: 1002 mHttpProxy = (ProxyInfo) msg.obj; 1003 handleLinkPropertiesUpdate(NO_CALLBACKS); 1004 break; 1005 1006 case CMD_SET_MULTICAST_FILTER: 1007 mMulticastFiltering = (boolean) msg.obj; 1008 break; 1009 1010 case DhcpClient.CMD_ON_QUIT: 1011 // Everything is already stopped. 1012 Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped)."); 1013 break; 1014 1015 default: 1016 return NOT_HANDLED; 1017 } 1018 return HANDLED; 1019 } 1020 } 1021 1022 class StoppingState extends State { 1023 @Override enter()1024 public void enter() { 1025 if (mDhcpClient == null) { 1026 // There's no DHCPv4 for which to wait; proceed to stopped. 1027 transitionTo(mStoppedState); 1028 } 1029 } 1030 1031 @Override processMessage(Message msg)1032 public boolean processMessage(Message msg) { 1033 switch (msg.what) { 1034 case DhcpClient.CMD_ON_QUIT: 1035 mDhcpClient = null; 1036 transitionTo(mStoppedState); 1037 break; 1038 1039 default: 1040 deferMessage(msg); 1041 } 1042 return HANDLED; 1043 } 1044 } 1045 1046 class StartedState extends State { 1047 private boolean mDhcpActionInFlight; 1048 1049 @Override enter()1050 public void enter() { 1051 mStartTimeMillis = SystemClock.elapsedRealtime(); 1052 1053 mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface, 1054 mCallback, mMulticastFiltering); 1055 // TODO: investigate the effects of any multicast filtering racing/interfering with the 1056 // rest of this IP configuration startup. 1057 if (mApfFilter == null) { 1058 mCallback.setFallbackMulticastFilter(mMulticastFiltering); 1059 } 1060 1061 if (mConfiguration.mProvisioningTimeoutMs > 0) { 1062 final long alarmTime = SystemClock.elapsedRealtime() + 1063 mConfiguration.mProvisioningTimeoutMs; 1064 mProvisioningTimeoutAlarm.schedule(alarmTime); 1065 } 1066 1067 if (mConfiguration.mEnableIPv6) { 1068 // TODO: Consider transitionTo(mStoppingState) if this fails. 1069 startIPv6(); 1070 } 1071 1072 if (mConfiguration.mEnableIPv4) { 1073 if (!startIPv4()) { 1074 transitionTo(mStoppingState); 1075 return; 1076 } 1077 } 1078 1079 if (mConfiguration.mUsingIpReachabilityMonitor) { 1080 mIpReachabilityMonitor = new IpReachabilityMonitor( 1081 mContext, 1082 mInterfaceName, 1083 new IpReachabilityMonitor.Callback() { 1084 @Override 1085 public void notifyLost(InetAddress ip, String logMsg) { 1086 mCallback.onReachabilityLost(logMsg); 1087 } 1088 }, 1089 mAvoidBadWifiTracker); 1090 } 1091 } 1092 1093 @Override exit()1094 public void exit() { 1095 mProvisioningTimeoutAlarm.cancel(); 1096 stopDhcpAction(); 1097 1098 if (mIpReachabilityMonitor != null) { 1099 mIpReachabilityMonitor.stop(); 1100 mIpReachabilityMonitor = null; 1101 } 1102 1103 if (mDhcpClient != null) { 1104 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP); 1105 mDhcpClient.doQuit(); 1106 } 1107 1108 if (mApfFilter != null) { 1109 mApfFilter.shutdown(); 1110 mApfFilter = null; 1111 } 1112 1113 resetLinkProperties(); 1114 } 1115 ensureDhcpAction()1116 private void ensureDhcpAction() { 1117 if (!mDhcpActionInFlight) { 1118 mCallback.onPreDhcpAction(); 1119 mDhcpActionInFlight = true; 1120 final long alarmTime = SystemClock.elapsedRealtime() + 1121 mConfiguration.mRequestedPreDhcpActionMs; 1122 mDhcpActionTimeoutAlarm.schedule(alarmTime); 1123 } 1124 } 1125 stopDhcpAction()1126 private void stopDhcpAction() { 1127 mDhcpActionTimeoutAlarm.cancel(); 1128 if (mDhcpActionInFlight) { 1129 mCallback.onPostDhcpAction(); 1130 mDhcpActionInFlight = false; 1131 } 1132 } 1133 1134 @Override processMessage(Message msg)1135 public boolean processMessage(Message msg) { 1136 switch (msg.what) { 1137 case CMD_STOP: 1138 transitionTo(mStoppingState); 1139 break; 1140 1141 case CMD_START: 1142 Log.e(mTag, "ALERT: START received in StartedState. Please fix caller."); 1143 break; 1144 1145 case CMD_CONFIRM: 1146 // TODO: Possibly introduce a second type of confirmation 1147 // that both probes (a) on-link neighbors and (b) does 1148 // a DHCPv4 RENEW. We used to do this on Wi-Fi framework 1149 // roams. 1150 if (mIpReachabilityMonitor != null) { 1151 mIpReachabilityMonitor.probeAll(); 1152 } 1153 break; 1154 1155 case EVENT_PRE_DHCP_ACTION_COMPLETE: 1156 // It's possible to reach here if, for example, someone 1157 // calls completedPreDhcpAction() after provisioning with 1158 // a static IP configuration. 1159 if (mDhcpClient != null) { 1160 mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE); 1161 } 1162 break; 1163 1164 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 1165 if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) { 1166 transitionTo(mStoppingState); 1167 } 1168 break; 1169 1170 case CMD_UPDATE_TCP_BUFFER_SIZES: 1171 mTcpBufferSizes = (String) msg.obj; 1172 // This cannot possibly change provisioning state. 1173 handleLinkPropertiesUpdate(SEND_CALLBACKS); 1174 break; 1175 1176 case CMD_UPDATE_HTTP_PROXY: 1177 mHttpProxy = (ProxyInfo) msg.obj; 1178 // This cannot possibly change provisioning state. 1179 handleLinkPropertiesUpdate(SEND_CALLBACKS); 1180 break; 1181 1182 case CMD_SET_MULTICAST_FILTER: { 1183 mMulticastFiltering = (boolean) msg.obj; 1184 if (mApfFilter != null) { 1185 mApfFilter.setMulticastFilter(mMulticastFiltering); 1186 } else { 1187 mCallback.setFallbackMulticastFilter(mMulticastFiltering); 1188 } 1189 break; 1190 } 1191 1192 case EVENT_PROVISIONING_TIMEOUT: 1193 handleProvisioningFailure(); 1194 break; 1195 1196 case EVENT_DHCPACTION_TIMEOUT: 1197 stopDhcpAction(); 1198 break; 1199 1200 case DhcpClient.CMD_PRE_DHCP_ACTION: 1201 if (mConfiguration.mRequestedPreDhcpActionMs > 0) { 1202 ensureDhcpAction(); 1203 } else { 1204 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 1205 } 1206 break; 1207 1208 case DhcpClient.CMD_CLEAR_LINKADDRESS: 1209 clearIPv4Address(); 1210 break; 1211 1212 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: { 1213 final LinkAddress ipAddress = (LinkAddress) msg.obj; 1214 if (setIPv4Address(ipAddress)) { 1215 mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED); 1216 } else { 1217 Log.e(mTag, "Failed to set IPv4 address!"); 1218 dispatchCallback(ProvisioningChange.LOST_PROVISIONING, 1219 new LinkProperties(mLinkProperties)); 1220 transitionTo(mStoppingState); 1221 } 1222 break; 1223 } 1224 1225 // This message is only received when: 1226 // 1227 // a) initial address acquisition succeeds, 1228 // b) renew succeeds or is NAK'd, 1229 // c) rebind succeeds or is NAK'd, or 1230 // c) the lease expires, 1231 // 1232 // but never when initial address acquisition fails. The latter 1233 // condition is now governed by the provisioning timeout. 1234 case DhcpClient.CMD_POST_DHCP_ACTION: 1235 stopDhcpAction(); 1236 1237 switch (msg.arg1) { 1238 case DhcpClient.DHCP_SUCCESS: 1239 handleIPv4Success((DhcpResults) msg.obj); 1240 break; 1241 case DhcpClient.DHCP_FAILURE: 1242 handleIPv4Failure(); 1243 break; 1244 default: 1245 Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1); 1246 } 1247 break; 1248 1249 case DhcpClient.CMD_ON_QUIT: 1250 // DHCPv4 quit early for some reason. 1251 Log.e(mTag, "Unexpected CMD_ON_QUIT."); 1252 mDhcpClient = null; 1253 break; 1254 1255 default: 1256 return NOT_HANDLED; 1257 } 1258 return HANDLED; 1259 } 1260 } 1261 } 1262