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.MultinetworkPolicyTracker; 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 private static final int MAX_PACKET_RECORDS = 100; 378 379 private static final boolean NO_CALLBACKS = false; 380 private static final boolean SEND_CALLBACKS = true; 381 382 // This must match the interface prefix in clatd.c. 383 // TODO: Revert this hack once IpManager and Nat464Xlat work in concert. 384 private static final String CLAT_PREFIX = "v4-"; 385 386 private final State mStoppedState = new StoppedState(); 387 private final State mStoppingState = new StoppingState(); 388 private final State mStartedState = new StartedState(); 389 private final State mRunningState = new RunningState(); 390 391 private final String mTag; 392 private final Context mContext; 393 private final String mInterfaceName; 394 private final String mClatInterfaceName; 395 @VisibleForTesting 396 protected final Callback mCallback; 397 private final INetworkManagementService mNwService; 398 private final NetlinkTracker mNetlinkTracker; 399 private final WakeupMessage mProvisioningTimeoutAlarm; 400 private final WakeupMessage mDhcpActionTimeoutAlarm; 401 private final MultinetworkPolicyTracker mMultinetworkPolicyTracker; 402 private final LocalLog mLocalLog; 403 private final LocalLog mConnectivityPacketLog; 404 private final MessageHandlingLogger mMsgStateLogger; 405 private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); 406 407 private NetworkInterface mNetworkInterface; 408 409 /** 410 * Non-final member variables accessed only from within our StateMachine. 411 */ 412 private LinkProperties mLinkProperties; 413 private ProvisioningConfiguration mConfiguration; 414 private IpReachabilityMonitor mIpReachabilityMonitor; 415 private DhcpClient mDhcpClient; 416 private DhcpResults mDhcpResults; 417 private String mTcpBufferSizes; 418 private ProxyInfo mHttpProxy; 419 private ApfFilter mApfFilter; 420 private boolean mMulticastFiltering; 421 private long mStartTimeMillis; 422 423 public IpManager(Context context, String ifName, Callback callback) 424 throws IllegalArgumentException { 425 this(context, ifName, callback, INetworkManagementService.Stub.asInterface( 426 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE))); 427 } 428 429 /** 430 * An expanded constructor, useful for dependency injection. 431 */ 432 public IpManager(Context context, String ifName, Callback callback, 433 INetworkManagementService nwService) throws IllegalArgumentException { 434 super(IpManager.class.getSimpleName() + "." + ifName); 435 mTag = getName(); 436 437 mContext = context; 438 mInterfaceName = ifName; 439 mClatInterfaceName = CLAT_PREFIX + ifName; 440 mCallback = new LoggingCallbackWrapper(callback); 441 mNwService = nwService; 442 443 mLocalLog = new LocalLog(MAX_LOG_RECORDS); 444 mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS); 445 mMsgStateLogger = new MessageHandlingLogger(); 446 447 mNetlinkTracker = new NetlinkTracker( 448 mInterfaceName, 449 new NetlinkTracker.Callback() { 450 @Override 451 public void update() { 452 sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED); 453 } 454 }) { 455 @Override 456 public void interfaceAdded(String iface) { 457 super.interfaceAdded(iface); 458 if (mClatInterfaceName.equals(iface)) { 459 mCallback.setNeighborDiscoveryOffload(false); 460 } else if (!mInterfaceName.equals(iface)) { 461 return; 462 } 463 464 final String msg = "interfaceAdded(" + iface +")"; 465 logMsg(msg); 466 } 467 468 @Override 469 public void interfaceRemoved(String iface) { 470 super.interfaceRemoved(iface); 471 // TODO: Also observe mInterfaceName going down and take some 472 // kind of appropriate action. 473 if (mClatInterfaceName.equals(iface)) { 474 // TODO: consider sending a message to the IpManager main 475 // StateMachine thread, in case "NDO enabled" state becomes 476 // tied to more things that 464xlat operation. 477 mCallback.setNeighborDiscoveryOffload(true); 478 } else if (!mInterfaceName.equals(iface)) { 479 return; 480 } 481 482 final String msg = "interfaceRemoved(" + iface +")"; 483 logMsg(msg); 484 } 485 486 private void logMsg(String msg) { 487 Log.d(mTag, msg); 488 getHandler().post(() -> { mLocalLog.log("OBSERVED " + msg); }); 489 } 490 }; 491 492 mLinkProperties = new LinkProperties(); 493 mLinkProperties.setInterfaceName(mInterfaceName); 494 495 mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(mContext, getHandler(), 496 () -> { mLocalLog.log("OBSERVED AvoidBadWifi changed"); }); 497 498 mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(), 499 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT); 500 mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(), 501 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT); 502 503 // Anything the StateMachine may access must have been instantiated 504 // before this point. 505 configureAndStartStateMachine(); 506 507 // Anything that may send messages to the StateMachine must only be 508 // configured to do so after the StateMachine has started (above). 509 startStateMachineUpdaters(); 510 } 511 512 private void configureAndStartStateMachine() { 513 addState(mStoppedState); 514 addState(mStartedState); 515 addState(mRunningState, mStartedState); 516 addState(mStoppingState); 517 518 setInitialState(mStoppedState); 519 520 super.start(); 521 } 522 523 private void startStateMachineUpdaters() { 524 try { 525 mNwService.registerObserver(mNetlinkTracker); 526 } catch (RemoteException e) { 527 logError("Couldn't register NetlinkTracker: %s", e); 528 } 529 530 mMultinetworkPolicyTracker.start(); 531 } 532 533 @Override 534 protected void onQuitting() { 535 mCallback.onQuit(); 536 } 537 538 // Shut down this IpManager instance altogether. 539 public void shutdown() { 540 stop(); 541 mMultinetworkPolicyTracker.shutdown(); 542 quit(); 543 } 544 545 public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() { 546 return new ProvisioningConfiguration.Builder(); 547 } 548 549 public void startProvisioning(ProvisioningConfiguration req) { 550 getNetworkInterface(); 551 552 mCallback.setNeighborDiscoveryOffload(true); 553 sendMessage(CMD_START, new ProvisioningConfiguration(req)); 554 } 555 556 // TODO: Delete this. 557 public void startProvisioning(StaticIpConfiguration staticIpConfig) { 558 startProvisioning(buildProvisioningConfiguration() 559 .withStaticConfiguration(staticIpConfig) 560 .build()); 561 } 562 563 public void startProvisioning() { 564 startProvisioning(new ProvisioningConfiguration()); 565 } 566 567 public void stop() { 568 sendMessage(CMD_STOP); 569 } 570 571 public void confirmConfiguration() { 572 sendMessage(CMD_CONFIRM); 573 } 574 575 public void completedPreDhcpAction() { 576 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 577 } 578 579 /** 580 * Set the TCP buffer sizes to use. 581 * 582 * This may be called, repeatedly, at any time before or after a call to 583 * #startProvisioning(). The setting is cleared upon calling #stop(). 584 */ 585 public void setTcpBufferSizes(String tcpBufferSizes) { 586 sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes); 587 } 588 589 /** 590 * Set the HTTP Proxy configuration to use. 591 * 592 * This may be called, repeatedly, at any time before or after a call to 593 * #startProvisioning(). The setting is cleared upon calling #stop(). 594 */ 595 public void setHttpProxy(ProxyInfo proxyInfo) { 596 sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo); 597 } 598 599 /** 600 * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering, 601 * if not, Callback.setFallbackMulticastFilter() is called. 602 */ 603 public void setMulticastFilter(boolean enabled) { 604 sendMessage(CMD_SET_MULTICAST_FILTER, enabled); 605 } 606 607 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 608 if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) { 609 // Execute confirmConfiguration() and take no further action. 610 confirmConfiguration(); 611 return; 612 } 613 614 // Thread-unsafe access to mApfFilter but just used for debugging. 615 final ApfFilter apfFilter = mApfFilter; 616 final ProvisioningConfiguration provisioningConfig = mConfiguration; 617 final ApfCapabilities apfCapabilities = (provisioningConfig != null) 618 ? provisioningConfig.mApfCapabilities : null; 619 620 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 621 pw.println(mTag + " APF dump:"); 622 pw.increaseIndent(); 623 if (apfFilter != null) { 624 apfFilter.dump(pw); 625 } else { 626 pw.print("No active ApfFilter; "); 627 if (provisioningConfig == null) { 628 pw.println("IpManager not yet started."); 629 } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) { 630 pw.println("Hardware does not support APF."); 631 } else { 632 pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities); 633 } 634 } 635 pw.decreaseIndent(); 636 637 pw.println(); 638 pw.println(mTag + " current ProvisioningConfiguration:"); 639 pw.increaseIndent(); 640 pw.println(Objects.toString(provisioningConfig, "N/A")); 641 pw.decreaseIndent(); 642 643 pw.println(); 644 pw.println(mTag + " StateMachine dump:"); 645 pw.increaseIndent(); 646 mLocalLog.readOnlyLocalLog().dump(fd, pw, args); 647 pw.decreaseIndent(); 648 649 pw.println(); 650 pw.println(mTag + " connectivity packet log:"); 651 pw.println(); 652 pw.println("Debug with python and scapy via:"); 653 pw.println("shell$ python"); 654 pw.println(">>> from scapy import all as scapy"); 655 pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()"); 656 pw.println(); 657 658 pw.increaseIndent(); 659 mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args); 660 pw.decreaseIndent(); 661 } 662 663 664 /** 665 * Internals. 666 */ 667 668 @Override 669 protected String getWhatToString(int what) { 670 return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what)); 671 } 672 673 @Override 674 protected String getLogRecString(Message msg) { 675 final String logLine = String.format( 676 "%s/%d %d %d %s [%s]", 677 mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(), 678 msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger); 679 680 final String richerLogLine = getWhatToString(msg.what) + " " + logLine; 681 mLocalLog.log(richerLogLine); 682 if (VDBG) { 683 Log.d(mTag, richerLogLine); 684 } 685 686 mMsgStateLogger.reset(); 687 return logLine; 688 } 689 690 @Override 691 protected boolean recordLogRec(Message msg) { 692 // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy, 693 // and we already log any LinkProperties change that results in an 694 // invocation of IpManager.Callback#onLinkPropertiesChange(). 695 final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED); 696 if (!shouldLog) { 697 mMsgStateLogger.reset(); 698 } 699 return shouldLog; 700 } 701 702 // TODO: Migrate all Log.e(...) to logError(...). 703 private void logError(String fmt, Object... args) { 704 final String msg = "ERROR " + String.format(fmt, args); 705 Log.e(mTag, msg); 706 mLocalLog.log(msg); 707 } 708 709 private void getNetworkInterface() { 710 try { 711 mNetworkInterface = NetworkInterface.getByName(mInterfaceName); 712 } catch (SocketException | NullPointerException e) { 713 // TODO: throw new IllegalStateException. 714 logError("Failed to get interface object: %s", e); 715 } 716 } 717 718 // This needs to be called with care to ensure that our LinkProperties 719 // are in sync with the actual LinkProperties of the interface. For example, 720 // we should only call this if we know for sure that there are no IP addresses 721 // assigned to the interface, etc. 722 private void resetLinkProperties() { 723 mNetlinkTracker.clearLinkProperties(); 724 mConfiguration = null; 725 mDhcpResults = null; 726 mTcpBufferSizes = ""; 727 mHttpProxy = null; 728 729 mLinkProperties = new LinkProperties(); 730 mLinkProperties.setInterfaceName(mInterfaceName); 731 } 732 733 private void recordMetric(final int type) { 734 if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); } 735 final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis; 736 mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration)); 737 } 738 739 // For now: use WifiStateMachine's historical notion of provisioned. 740 private static boolean isProvisioned(LinkProperties lp) { 741 // For historical reasons, we should connect even if all we have is 742 // an IPv4 address and nothing else. 743 return lp.isProvisioned() || lp.hasIPv4Address(); 744 } 745 746 // TODO: Investigate folding all this into the existing static function 747 // LinkProperties.compareProvisioning() or some other single function that 748 // takes two LinkProperties objects and returns a ProvisioningChange 749 // object that is a correct and complete assessment of what changed, taking 750 // account of the asymmetries described in the comments in this function. 751 // Then switch to using it everywhere (IpReachabilityMonitor, etc.). 752 private ProvisioningChange compareProvisioning( 753 LinkProperties oldLp, LinkProperties newLp) { 754 ProvisioningChange delta; 755 756 final boolean wasProvisioned = isProvisioned(oldLp); 757 final boolean isProvisioned = isProvisioned(newLp); 758 759 if (!wasProvisioned && isProvisioned) { 760 delta = ProvisioningChange.GAINED_PROVISIONING; 761 } else if (wasProvisioned && isProvisioned) { 762 delta = ProvisioningChange.STILL_PROVISIONED; 763 } else if (!wasProvisioned && !isProvisioned) { 764 delta = ProvisioningChange.STILL_NOT_PROVISIONED; 765 } else { 766 // (wasProvisioned && !isProvisioned) 767 // 768 // Note that this is true even if we lose a configuration element 769 // (e.g., a default gateway) that would not be required to advance 770 // into provisioned state. This is intended: if we have a default 771 // router and we lose it, that's a sure sign of a problem, but if 772 // we connect to a network with no IPv4 DNS servers, we consider 773 // that to be a network without DNS servers and connect anyway. 774 // 775 // See the comment below. 776 delta = ProvisioningChange.LOST_PROVISIONING; 777 } 778 779 final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned(); 780 final boolean lostIPv4Address = oldLp.hasIPv4Address() && !newLp.hasIPv4Address(); 781 final boolean lostIPv6Router = oldLp.hasIPv6DefaultRoute() && !newLp.hasIPv6DefaultRoute(); 782 783 // If bad wifi avoidance is disabled, then ignore IPv6 loss of 784 // provisioning. Otherwise, when a hotspot that loses Internet 785 // access sends out a 0-lifetime RA to its clients, the clients 786 // will disconnect and then reconnect, avoiding the bad hotspot, 787 // instead of getting stuck on the bad hotspot. http://b/31827713 . 788 // 789 // This is incorrect because if the hotspot then regains Internet 790 // access with a different prefix, TCP connections on the 791 // deprecated addresses will remain stuck. 792 // 793 // Note that we can still be disconnected by IpReachabilityMonitor 794 // if the IPv6 default gateway (but not the IPv6 DNS servers; see 795 // accompanying code in IpReachabilityMonitor) is unreachable. 796 final boolean ignoreIPv6ProvisioningLoss = !mMultinetworkPolicyTracker.getAvoidBadWifi(); 797 798 // Additionally: 799 // 800 // Partial configurations (e.g., only an IPv4 address with no DNS 801 // servers and no default route) are accepted as long as DHCPv4 802 // succeeds. On such a network, isProvisioned() will always return 803 // false, because the configuration is not complete, but we want to 804 // connect anyway. It might be a disconnected network such as a 805 // Chromecast or a wireless printer, for example. 806 // 807 // Because on such a network isProvisioned() will always return false, 808 // delta will never be LOST_PROVISIONING. So check for loss of 809 // provisioning here too. 810 if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) { 811 delta = ProvisioningChange.LOST_PROVISIONING; 812 } 813 814 // Additionally: 815 // 816 // If the previous link properties had a global IPv6 address and an 817 // IPv6 default route then also consider the loss of that default route 818 // to be a loss of provisioning. See b/27962810. 819 if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) { 820 delta = ProvisioningChange.LOST_PROVISIONING; 821 } 822 823 return delta; 824 } 825 826 private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) { 827 switch (delta) { 828 case GAINED_PROVISIONING: 829 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); } 830 recordMetric(IpManagerEvent.PROVISIONING_OK); 831 mCallback.onProvisioningSuccess(newLp); 832 break; 833 834 case LOST_PROVISIONING: 835 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } 836 recordMetric(IpManagerEvent.PROVISIONING_FAIL); 837 mCallback.onProvisioningFailure(newLp); 838 break; 839 840 default: 841 if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); } 842 mCallback.onLinkPropertiesChange(newLp); 843 break; 844 } 845 } 846 847 // Updates all IpManager-related state concerned with LinkProperties. 848 // Returns a ProvisioningChange for possibly notifying other interested 849 // parties that are not fronted by IpManager. 850 private ProvisioningChange setLinkProperties(LinkProperties newLp) { 851 if (mApfFilter != null) { 852 mApfFilter.setLinkProperties(newLp); 853 } 854 if (mIpReachabilityMonitor != null) { 855 mIpReachabilityMonitor.updateLinkProperties(newLp); 856 } 857 858 ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp); 859 mLinkProperties = new LinkProperties(newLp); 860 861 if (delta == ProvisioningChange.GAINED_PROVISIONING) { 862 // TODO: Add a proper ProvisionedState and cancel the alarm in 863 // its enter() method. 864 mProvisioningTimeoutAlarm.cancel(); 865 } 866 867 return delta; 868 } 869 870 private boolean linkPropertiesUnchanged(LinkProperties newLp) { 871 return Objects.equals(newLp, mLinkProperties); 872 } 873 874 private LinkProperties assembleLinkProperties() { 875 // [1] Create a new LinkProperties object to populate. 876 LinkProperties newLp = new LinkProperties(); 877 newLp.setInterfaceName(mInterfaceName); 878 879 // [2] Pull in data from netlink: 880 // - IPv4 addresses 881 // - IPv6 addresses 882 // - IPv6 routes 883 // - IPv6 DNS servers 884 // 885 // N.B.: this is fundamentally race-prone and should be fixed by 886 // changing NetlinkTracker from a hybrid edge/level model to an 887 // edge-only model, or by giving IpManager its own netlink socket(s) 888 // so as to track all required information directly. 889 LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties(); 890 newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses()); 891 for (RouteInfo route : netlinkLinkProperties.getRoutes()) { 892 newLp.addRoute(route); 893 } 894 addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers()); 895 896 // [3] Add in data from DHCPv4, if available. 897 // 898 // mDhcpResults is never shared with any other owner so we don't have 899 // to worry about concurrent modification. 900 if (mDhcpResults != null) { 901 for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) { 902 newLp.addRoute(route); 903 } 904 addAllReachableDnsServers(newLp, mDhcpResults.dnsServers); 905 newLp.setDomains(mDhcpResults.domains); 906 907 if (mDhcpResults.mtu != 0) { 908 newLp.setMtu(mDhcpResults.mtu); 909 } 910 } 911 912 // [4] Add in TCP buffer sizes and HTTP Proxy config, if available. 913 if (!TextUtils.isEmpty(mTcpBufferSizes)) { 914 newLp.setTcpBufferSizes(mTcpBufferSizes); 915 } 916 if (mHttpProxy != null) { 917 newLp.setHttpProxy(mHttpProxy); 918 } 919 920 if (VDBG) { 921 Log.d(mTag, "newLp{" + newLp + "}"); 922 } 923 return newLp; 924 } 925 926 private static void addAllReachableDnsServers( 927 LinkProperties lp, Iterable<InetAddress> dnses) { 928 // TODO: Investigate deleting this reachability check. We should be 929 // able to pass everything down to netd and let netd do evaluation 930 // and RFC6724-style sorting. 931 for (InetAddress dns : dnses) { 932 if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) { 933 lp.addDnsServer(dns); 934 } 935 } 936 } 937 938 // Returns false if we have lost provisioning, true otherwise. 939 private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) { 940 final LinkProperties newLp = assembleLinkProperties(); 941 if (linkPropertiesUnchanged(newLp)) { 942 return true; 943 } 944 final ProvisioningChange delta = setLinkProperties(newLp); 945 if (sendCallbacks) { 946 dispatchCallback(delta, newLp); 947 } 948 return (delta != ProvisioningChange.LOST_PROVISIONING); 949 } 950 951 private boolean setIPv4Address(LinkAddress address) { 952 final InterfaceConfiguration ifcg = new InterfaceConfiguration(); 953 ifcg.setLinkAddress(address); 954 try { 955 mNwService.setInterfaceConfig(mInterfaceName, ifcg); 956 if (VDBG) Log.d(mTag, "IPv4 configuration succeeded"); 957 } catch (IllegalStateException | RemoteException e) { 958 logError("IPv4 configuration failed: %s", e); 959 return false; 960 } 961 return true; 962 } 963 964 private void clearIPv4Address() { 965 try { 966 final InterfaceConfiguration ifcg = new InterfaceConfiguration(); 967 ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0")); 968 mNwService.setInterfaceConfig(mInterfaceName, ifcg); 969 } catch (IllegalStateException | RemoteException e) { 970 logError("Failed to clear IPv4 address on interface %s: %s", mInterfaceName, e); 971 } 972 } 973 974 private void handleIPv4Success(DhcpResults dhcpResults) { 975 mDhcpResults = new DhcpResults(dhcpResults); 976 final LinkProperties newLp = assembleLinkProperties(); 977 final ProvisioningChange delta = setLinkProperties(newLp); 978 979 if (VDBG) { 980 Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); 981 } 982 mCallback.onNewDhcpResults(dhcpResults); 983 dispatchCallback(delta, newLp); 984 } 985 986 private void handleIPv4Failure() { 987 // TODO: Investigate deleting this clearIPv4Address() call. 988 // 989 // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances 990 // that could trigger a call to this function. If we missed handling 991 // that message in StartedState for some reason we would still clear 992 // any addresses upon entry to StoppedState. 993 clearIPv4Address(); 994 mDhcpResults = null; 995 if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); } 996 mCallback.onNewDhcpResults(null); 997 998 handleProvisioningFailure(); 999 } 1000 1001 private void handleProvisioningFailure() { 1002 final LinkProperties newLp = assembleLinkProperties(); 1003 ProvisioningChange delta = setLinkProperties(newLp); 1004 // If we've gotten here and we're still not provisioned treat that as 1005 // a total loss of provisioning. 1006 // 1007 // Either (a) static IP configuration failed or (b) DHCPv4 failed AND 1008 // there was no usable IPv6 obtained before a non-zero provisioning 1009 // timeout expired. 1010 // 1011 // Regardless: GAME OVER. 1012 if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) { 1013 delta = ProvisioningChange.LOST_PROVISIONING; 1014 } 1015 1016 dispatchCallback(delta, newLp); 1017 if (delta == ProvisioningChange.LOST_PROVISIONING) { 1018 transitionTo(mStoppingState); 1019 } 1020 } 1021 1022 private void doImmediateProvisioningFailure(int failureType) { 1023 if (DBG) { Log.e(mTag, "onProvisioningFailure(): " + failureType); } 1024 recordMetric(failureType); 1025 mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties)); 1026 } 1027 1028 private boolean startIPv4() { 1029 // If we have a StaticIpConfiguration attempt to apply it and 1030 // handle the result accordingly. 1031 if (mConfiguration.mStaticIpConfig != null) { 1032 if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) { 1033 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig)); 1034 } else { 1035 return false; 1036 } 1037 } else { 1038 // Start DHCPv4. 1039 mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName); 1040 mDhcpClient.registerForPreDhcpNotification(); 1041 mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); 1042 } 1043 1044 return true; 1045 } 1046 1047 private boolean startIPv6() { 1048 // Set privacy extensions. 1049 try { 1050 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); 1051 mNwService.enableIpv6(mInterfaceName); 1052 } catch (RemoteException re) { 1053 logError("Unable to change interface settings: %s", re); 1054 return false; 1055 } catch (IllegalStateException ie) { 1056 logError("Unable to change interface settings: %s", ie); 1057 return false; 1058 } 1059 1060 return true; 1061 } 1062 1063 private boolean startIpReachabilityMonitor() { 1064 try { 1065 mIpReachabilityMonitor = new IpReachabilityMonitor( 1066 mContext, 1067 mInterfaceName, 1068 new IpReachabilityMonitor.Callback() { 1069 @Override 1070 public void notifyLost(InetAddress ip, String logMsg) { 1071 mCallback.onReachabilityLost(logMsg); 1072 } 1073 }, 1074 mMultinetworkPolicyTracker); 1075 } catch (IllegalArgumentException iae) { 1076 // Failed to start IpReachabilityMonitor. Log it and call 1077 // onProvisioningFailure() immediately. 1078 // 1079 // See http://b/31038971. 1080 logError("IpReachabilityMonitor failure: %s", iae); 1081 mIpReachabilityMonitor = null; 1082 } 1083 1084 return (mIpReachabilityMonitor != null); 1085 } 1086 1087 private void stopAllIP() { 1088 // We don't need to worry about routes, just addresses, because: 1089 // - disableIpv6() will clear autoconf IPv6 routes as well, and 1090 // - we don't get IPv4 routes from netlink 1091 // so we neither react to nor need to wait for changes in either. 1092 1093 try { 1094 mNwService.disableIpv6(mInterfaceName); 1095 } catch (Exception e) { 1096 logError("Failed to disable IPv6: %s", e); 1097 } 1098 1099 try { 1100 mNwService.clearInterfaceAddresses(mInterfaceName); 1101 } catch (Exception e) { 1102 logError("Failed to clear addresses: %s", e); 1103 } 1104 } 1105 1106 class StoppedState extends State { 1107 @Override 1108 public void enter() { 1109 stopAllIP(); 1110 1111 resetLinkProperties(); 1112 if (mStartTimeMillis > 0) { 1113 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE); 1114 mStartTimeMillis = 0; 1115 } 1116 } 1117 1118 @Override 1119 public boolean processMessage(Message msg) { 1120 switch (msg.what) { 1121 case CMD_STOP: 1122 break; 1123 1124 case CMD_START: 1125 mConfiguration = (ProvisioningConfiguration) msg.obj; 1126 transitionTo(mStartedState); 1127 break; 1128 1129 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 1130 handleLinkPropertiesUpdate(NO_CALLBACKS); 1131 break; 1132 1133 case CMD_UPDATE_TCP_BUFFER_SIZES: 1134 mTcpBufferSizes = (String) msg.obj; 1135 handleLinkPropertiesUpdate(NO_CALLBACKS); 1136 break; 1137 1138 case CMD_UPDATE_HTTP_PROXY: 1139 mHttpProxy = (ProxyInfo) msg.obj; 1140 handleLinkPropertiesUpdate(NO_CALLBACKS); 1141 break; 1142 1143 case CMD_SET_MULTICAST_FILTER: 1144 mMulticastFiltering = (boolean) msg.obj; 1145 break; 1146 1147 case DhcpClient.CMD_ON_QUIT: 1148 // Everything is already stopped. 1149 Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped)."); 1150 break; 1151 1152 default: 1153 return NOT_HANDLED; 1154 } 1155 1156 mMsgStateLogger.handled(this, getCurrentState()); 1157 return HANDLED; 1158 } 1159 } 1160 1161 class StoppingState extends State { 1162 @Override 1163 public void enter() { 1164 if (mDhcpClient == null) { 1165 // There's no DHCPv4 for which to wait; proceed to stopped. 1166 transitionTo(mStoppedState); 1167 } 1168 } 1169 1170 @Override 1171 public boolean processMessage(Message msg) { 1172 switch (msg.what) { 1173 case CMD_STOP: 1174 break; 1175 1176 case DhcpClient.CMD_CLEAR_LINKADDRESS: 1177 clearIPv4Address(); 1178 break; 1179 1180 case DhcpClient.CMD_ON_QUIT: 1181 mDhcpClient = null; 1182 transitionTo(mStoppedState); 1183 break; 1184 1185 default: 1186 deferMessage(msg); 1187 } 1188 1189 mMsgStateLogger.handled(this, getCurrentState()); 1190 return HANDLED; 1191 } 1192 } 1193 1194 class StartedState extends State { 1195 @Override 1196 public void enter() { 1197 mStartTimeMillis = SystemClock.elapsedRealtime(); 1198 1199 if (mConfiguration.mProvisioningTimeoutMs > 0) { 1200 final long alarmTime = SystemClock.elapsedRealtime() + 1201 mConfiguration.mProvisioningTimeoutMs; 1202 mProvisioningTimeoutAlarm.schedule(alarmTime); 1203 } 1204 1205 if (readyToProceed()) { 1206 transitionTo(mRunningState); 1207 } else { 1208 // Clear all IPv4 and IPv6 before proceeding to RunningState. 1209 // Clean up any leftover state from an abnormal exit from 1210 // tethering or during an IpManager restart. 1211 stopAllIP(); 1212 } 1213 } 1214 1215 @Override 1216 public void exit() { 1217 mProvisioningTimeoutAlarm.cancel(); 1218 } 1219 1220 @Override 1221 public boolean processMessage(Message msg) { 1222 switch (msg.what) { 1223 case CMD_STOP: 1224 transitionTo(mStoppingState); 1225 break; 1226 1227 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 1228 handleLinkPropertiesUpdate(NO_CALLBACKS); 1229 if (readyToProceed()) { 1230 transitionTo(mRunningState); 1231 } 1232 break; 1233 1234 case EVENT_PROVISIONING_TIMEOUT: 1235 handleProvisioningFailure(); 1236 break; 1237 1238 default: 1239 // It's safe to process messages out of order because the 1240 // only message that can both 1241 // a) be received at this time and 1242 // b) affect provisioning state 1243 // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above). 1244 deferMessage(msg); 1245 } 1246 1247 mMsgStateLogger.handled(this, getCurrentState()); 1248 return HANDLED; 1249 } 1250 1251 boolean readyToProceed() { 1252 return (!mLinkProperties.hasIPv4Address() && 1253 !mLinkProperties.hasGlobalIPv6Address()); 1254 } 1255 } 1256 1257 class RunningState extends State { 1258 private ConnectivityPacketTracker mPacketTracker; 1259 private boolean mDhcpActionInFlight; 1260 1261 @Override 1262 public void enter() { 1263 mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface, 1264 mCallback, mMulticastFiltering); 1265 // TODO: investigate the effects of any multicast filtering racing/interfering with the 1266 // rest of this IP configuration startup. 1267 if (mApfFilter == null) { 1268 mCallback.setFallbackMulticastFilter(mMulticastFiltering); 1269 } 1270 1271 mPacketTracker = createPacketTracker(); 1272 if (mPacketTracker != null) mPacketTracker.start(); 1273 1274 if (mConfiguration.mEnableIPv6 && !startIPv6()) { 1275 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); 1276 transitionTo(mStoppingState); 1277 return; 1278 } 1279 1280 if (mConfiguration.mEnableIPv4 && !startIPv4()) { 1281 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4); 1282 transitionTo(mStoppingState); 1283 return; 1284 } 1285 1286 if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) { 1287 doImmediateProvisioningFailure( 1288 IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR); 1289 transitionTo(mStoppingState); 1290 return; 1291 } 1292 } 1293 1294 @Override 1295 public void exit() { 1296 stopDhcpAction(); 1297 1298 if (mIpReachabilityMonitor != null) { 1299 mIpReachabilityMonitor.stop(); 1300 mIpReachabilityMonitor = null; 1301 } 1302 1303 if (mDhcpClient != null) { 1304 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP); 1305 mDhcpClient.doQuit(); 1306 } 1307 1308 if (mPacketTracker != null) { 1309 mPacketTracker.stop(); 1310 mPacketTracker = null; 1311 } 1312 1313 if (mApfFilter != null) { 1314 mApfFilter.shutdown(); 1315 mApfFilter = null; 1316 } 1317 1318 resetLinkProperties(); 1319 } 1320 1321 private ConnectivityPacketTracker createPacketTracker() { 1322 try { 1323 return new ConnectivityPacketTracker(mNetworkInterface, mConnectivityPacketLog); 1324 } catch (IllegalArgumentException e) { 1325 return null; 1326 } 1327 } 1328 1329 private void ensureDhcpAction() { 1330 if (!mDhcpActionInFlight) { 1331 mCallback.onPreDhcpAction(); 1332 mDhcpActionInFlight = true; 1333 final long alarmTime = SystemClock.elapsedRealtime() + 1334 mConfiguration.mRequestedPreDhcpActionMs; 1335 mDhcpActionTimeoutAlarm.schedule(alarmTime); 1336 } 1337 } 1338 1339 private void stopDhcpAction() { 1340 mDhcpActionTimeoutAlarm.cancel(); 1341 if (mDhcpActionInFlight) { 1342 mCallback.onPostDhcpAction(); 1343 mDhcpActionInFlight = false; 1344 } 1345 } 1346 1347 @Override 1348 public boolean processMessage(Message msg) { 1349 switch (msg.what) { 1350 case CMD_STOP: 1351 transitionTo(mStoppingState); 1352 break; 1353 1354 case CMD_START: 1355 Log.e(mTag, "ALERT: START received in StartedState. Please fix caller."); 1356 break; 1357 1358 case CMD_CONFIRM: 1359 // TODO: Possibly introduce a second type of confirmation 1360 // that both probes (a) on-link neighbors and (b) does 1361 // a DHCPv4 RENEW. We used to do this on Wi-Fi framework 1362 // roams. 1363 if (mIpReachabilityMonitor != null) { 1364 mIpReachabilityMonitor.probeAll(); 1365 } 1366 break; 1367 1368 case EVENT_PRE_DHCP_ACTION_COMPLETE: 1369 // It's possible to reach here if, for example, someone 1370 // calls completedPreDhcpAction() after provisioning with 1371 // a static IP configuration. 1372 if (mDhcpClient != null) { 1373 mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE); 1374 } 1375 break; 1376 1377 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 1378 if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) { 1379 transitionTo(mStoppingState); 1380 } 1381 break; 1382 1383 case CMD_UPDATE_TCP_BUFFER_SIZES: 1384 mTcpBufferSizes = (String) msg.obj; 1385 // This cannot possibly change provisioning state. 1386 handleLinkPropertiesUpdate(SEND_CALLBACKS); 1387 break; 1388 1389 case CMD_UPDATE_HTTP_PROXY: 1390 mHttpProxy = (ProxyInfo) msg.obj; 1391 // This cannot possibly change provisioning state. 1392 handleLinkPropertiesUpdate(SEND_CALLBACKS); 1393 break; 1394 1395 case CMD_SET_MULTICAST_FILTER: { 1396 mMulticastFiltering = (boolean) msg.obj; 1397 if (mApfFilter != null) { 1398 mApfFilter.setMulticastFilter(mMulticastFiltering); 1399 } else { 1400 mCallback.setFallbackMulticastFilter(mMulticastFiltering); 1401 } 1402 break; 1403 } 1404 1405 case EVENT_DHCPACTION_TIMEOUT: 1406 stopDhcpAction(); 1407 break; 1408 1409 case DhcpClient.CMD_PRE_DHCP_ACTION: 1410 if (mConfiguration.mRequestedPreDhcpActionMs > 0) { 1411 ensureDhcpAction(); 1412 } else { 1413 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 1414 } 1415 break; 1416 1417 case DhcpClient.CMD_CLEAR_LINKADDRESS: 1418 clearIPv4Address(); 1419 break; 1420 1421 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: { 1422 final LinkAddress ipAddress = (LinkAddress) msg.obj; 1423 if (setIPv4Address(ipAddress)) { 1424 mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED); 1425 } else { 1426 logError("Failed to set IPv4 address."); 1427 dispatchCallback(ProvisioningChange.LOST_PROVISIONING, 1428 new LinkProperties(mLinkProperties)); 1429 transitionTo(mStoppingState); 1430 } 1431 break; 1432 } 1433 1434 // This message is only received when: 1435 // 1436 // a) initial address acquisition succeeds, 1437 // b) renew succeeds or is NAK'd, 1438 // c) rebind succeeds or is NAK'd, or 1439 // c) the lease expires, 1440 // 1441 // but never when initial address acquisition fails. The latter 1442 // condition is now governed by the provisioning timeout. 1443 case DhcpClient.CMD_POST_DHCP_ACTION: 1444 stopDhcpAction(); 1445 1446 switch (msg.arg1) { 1447 case DhcpClient.DHCP_SUCCESS: 1448 handleIPv4Success((DhcpResults) msg.obj); 1449 break; 1450 case DhcpClient.DHCP_FAILURE: 1451 handleIPv4Failure(); 1452 break; 1453 default: 1454 Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1); 1455 } 1456 break; 1457 1458 case DhcpClient.CMD_ON_QUIT: 1459 // DHCPv4 quit early for some reason. 1460 Log.e(mTag, "Unexpected CMD_ON_QUIT."); 1461 mDhcpClient = null; 1462 break; 1463 1464 default: 1465 return NOT_HANDLED; 1466 } 1467 1468 mMsgStateLogger.handled(this, getCurrentState()); 1469 return HANDLED; 1470 } 1471 } 1472 1473 private static class MessageHandlingLogger { 1474 public String processedInState; 1475 public String receivedInState; 1476 1477 public void reset() { 1478 processedInState = null; 1479 receivedInState = null; 1480 } 1481 1482 public void handled(State processedIn, IState receivedIn) { 1483 processedInState = processedIn.getClass().getSimpleName(); 1484 receivedInState = receivedIn.getName(); 1485 } 1486 1487 public String toString() { 1488 return String.format("rcvd_in=%s, proc_in=%s", 1489 receivedInState, processedInState); 1490 } 1491 } 1492 } 1493