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