IpManager.java revision 19e84f7b750c97d5d7440af16c7ba18bf759e5dc
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 17package android.net.ip; 18 19import com.android.internal.util.MessageUtils; 20 21import android.content.Context; 22import android.net.apf.ApfCapabilities; 23import android.net.apf.ApfFilter; 24import android.net.DhcpResults; 25import android.net.InterfaceConfiguration; 26import android.net.LinkAddress; 27import android.net.LinkProperties; 28import android.net.LinkProperties.ProvisioningChange; 29import android.net.ProxyInfo; 30import android.net.RouteInfo; 31import android.net.StaticIpConfiguration; 32import android.net.dhcp.DhcpClient; 33import android.os.INetworkManagementService; 34import android.os.Message; 35import android.os.RemoteException; 36import android.os.ServiceManager; 37import android.text.TextUtils; 38import android.util.Log; 39import android.util.SparseArray; 40 41import com.android.internal.annotations.GuardedBy; 42import com.android.internal.annotations.VisibleForTesting; 43import com.android.internal.util.IndentingPrintWriter; 44import com.android.internal.util.State; 45import com.android.internal.util.StateMachine; 46import com.android.server.net.NetlinkTracker; 47 48import java.io.FileDescriptor; 49import java.io.PrintWriter; 50import java.net.InetAddress; 51import java.net.NetworkInterface; 52import java.net.SocketException; 53import java.util.Objects; 54 55 56/** 57 * IpManager 58 * 59 * This class provides the interface to IP-layer provisioning and maintenance 60 * functionality that can be used by transport layers like Wi-Fi, Ethernet, 61 * et cetera. 62 * 63 * [ Lifetime ] 64 * IpManager is designed to be instantiated as soon as the interface name is 65 * known and can be as long-lived as the class containing it (i.e. declaring 66 * it "private final" is okay). 67 * 68 * @hide 69 */ 70public class IpManager extends StateMachine { 71 private static final boolean DBG = false; 72 private static final boolean VDBG = false; 73 74 // For message logging. 75 private static final Class[] sMessageClasses = { IpManager.class, DhcpClient.class }; 76 private static final SparseArray<String> sWhatToString = 77 MessageUtils.findMessageNames(sMessageClasses); 78 79 /** 80 * Callbacks for handling IpManager events. 81 */ 82 public static class Callback { 83 // In order to receive onPreDhcpAction(), call #withPreDhcpAction() 84 // when constructing a ProvisioningConfiguration. 85 // 86 // Implementations of onPreDhcpAction() must call 87 // IpManager#completedPreDhcpAction() to indicate that DHCP is clear 88 // to proceed. 89 public void onPreDhcpAction() {} 90 public void onPostDhcpAction() {} 91 92 // This is purely advisory and not an indication of provisioning 93 // success or failure. This is only here for callers that want to 94 // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress). 95 // DHCPv4 or static IPv4 configuration failure or success can be 96 // determined by whether or not the passed-in DhcpResults object is 97 // null or not. 98 public void onNewDhcpResults(DhcpResults dhcpResults) {} 99 100 public void onProvisioningSuccess(LinkProperties newLp) {} 101 public void onProvisioningFailure(LinkProperties newLp) {} 102 103 // Invoked on LinkProperties changes. 104 public void onLinkPropertiesChange(LinkProperties newLp) {} 105 106 // Called when the internal IpReachabilityMonitor (if enabled) has 107 // detected the loss of a critical number of required neighbors. 108 public void onReachabilityLost(String logMsg) {} 109 110 // Called when the IpManager state machine terminates. 111 public void onQuit() {} 112 113 // Install an APF program to filter incoming packets. 114 public void installPacketFilter(byte[] filter) {} 115 116 // If multicast filtering cannot be accomplished with APF, this function will be called to 117 // actuate multicast filtering using another means. 118 public void setFallbackMulticastFilter(boolean enabled) {} 119 120 // Enabled/disable Neighbor Discover offload functionality. This is 121 // called, for example, whenever 464xlat is being started or stopped. 122 public void setNeighborDiscoveryOffload(boolean enable) {} 123 } 124 125 public static class WaitForProvisioningCallback extends Callback { 126 private LinkProperties mCallbackLinkProperties; 127 128 public LinkProperties waitForProvisioning() { 129 synchronized (this) { 130 try { 131 wait(); 132 } catch (InterruptedException e) {} 133 return mCallbackLinkProperties; 134 } 135 } 136 137 @Override 138 public void onProvisioningSuccess(LinkProperties newLp) { 139 synchronized (this) { 140 mCallbackLinkProperties = newLp; 141 notify(); 142 } 143 } 144 145 @Override 146 public void onProvisioningFailure(LinkProperties newLp) { 147 synchronized (this) { 148 mCallbackLinkProperties = null; 149 notify(); 150 } 151 } 152 } 153 154 /** 155 * This class encapsulates parameters to be passed to 156 * IpManager#startProvisioning(). A defensive copy is made by IpManager 157 * and the values specified herein are in force until IpManager#stop() 158 * is called. 159 * 160 * Example use: 161 * 162 * final ProvisioningConfiguration config = 163 * mIpManager.buildProvisioningConfiguration() 164 * .withPreDhcpAction() 165 * .build(); 166 * mIpManager.startProvisioning(config); 167 * ... 168 * mIpManager.stop(); 169 * 170 * The specified provisioning configuration will only be active until 171 * IpManager#stop() is called. Future calls to IpManager#startProvisioning() 172 * must specify the configuration again. 173 */ 174 public static class ProvisioningConfiguration { 175 176 public static class Builder { 177 private ProvisioningConfiguration mConfig = new ProvisioningConfiguration(); 178 179 public Builder withoutIpReachabilityMonitor() { 180 mConfig.mUsingIpReachabilityMonitor = false; 181 return this; 182 } 183 184 public Builder withPreDhcpAction() { 185 mConfig.mRequestedPreDhcpAction = true; 186 return this; 187 } 188 189 public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) { 190 mConfig.mStaticIpConfig = staticConfig; 191 return this; 192 } 193 194 public Builder withApfCapabilities(ApfCapabilities apfCapabilities) { 195 mConfig.mApfCapabilities = apfCapabilities; 196 return this; 197 } 198 199 public ProvisioningConfiguration build() { 200 return new ProvisioningConfiguration(mConfig); 201 } 202 } 203 204 /* package */ boolean mUsingIpReachabilityMonitor = true; 205 /* package */ boolean mRequestedPreDhcpAction; 206 /* package */ StaticIpConfiguration mStaticIpConfig; 207 /* package */ ApfCapabilities mApfCapabilities; 208 209 public ProvisioningConfiguration() {} 210 211 public ProvisioningConfiguration(ProvisioningConfiguration other) { 212 mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor; 213 mRequestedPreDhcpAction = other.mRequestedPreDhcpAction; 214 mStaticIpConfig = other.mStaticIpConfig; 215 mApfCapabilities = other.mApfCapabilities; 216 } 217 } 218 219 public static final String DUMP_ARG = "ipmanager"; 220 221 private static final int CMD_STOP = 1; 222 private static final int CMD_START = 2; 223 private static final int CMD_CONFIRM = 3; 224 private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 4; 225 // Sent by NetlinkTracker to communicate netlink events. 226 private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5; 227 private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6; 228 private static final int CMD_UPDATE_HTTP_PROXY = 7; 229 private static final int CMD_SET_MULTICAST_FILTER = 8; 230 231 private static final int MAX_LOG_RECORDS = 1000; 232 233 private static final boolean NO_CALLBACKS = false; 234 private static final boolean SEND_CALLBACKS = true; 235 236 // This must match the interface prefix in clatd.c. 237 // TODO: Revert this hack once IpManager and Nat464Xlat work in concert. 238 private static final String CLAT_PREFIX = "v4-"; 239 240 private final Object mLock = new Object(); 241 private final State mStoppedState = new StoppedState(); 242 private final State mStoppingState = new StoppingState(); 243 private final State mStartedState = new StartedState(); 244 245 private final String mTag; 246 private final Context mContext; 247 private final String mInterfaceName; 248 private final String mClatInterfaceName; 249 @VisibleForTesting 250 protected final Callback mCallback; 251 private final INetworkManagementService mNwService; 252 private final NetlinkTracker mNetlinkTracker; 253 254 private NetworkInterface mNetworkInterface; 255 256 /** 257 * Non-final member variables accessed only from within our StateMachine. 258 */ 259 private ProvisioningConfiguration mConfiguration; 260 private IpReachabilityMonitor mIpReachabilityMonitor; 261 private DhcpClient mDhcpClient; 262 private DhcpResults mDhcpResults; 263 private String mTcpBufferSizes; 264 private ProxyInfo mHttpProxy; 265 private ApfFilter mApfFilter; 266 private boolean mMulticastFiltering; 267 268 /** 269 * Member variables accessed both from within the StateMachine thread 270 * and via accessors from other threads. 271 */ 272 @GuardedBy("mLock") 273 private LinkProperties mLinkProperties; 274 275 public IpManager(Context context, String ifName, Callback callback) 276 throws IllegalArgumentException { 277 this(context, ifName, callback, INetworkManagementService.Stub.asInterface( 278 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE))); 279 } 280 281 /** 282 * An expanded constructor, useful for dependency injection. 283 */ 284 public IpManager(Context context, String ifName, Callback callback, 285 INetworkManagementService nwService) throws IllegalArgumentException { 286 super(IpManager.class.getSimpleName() + "." + ifName); 287 mTag = getName(); 288 289 mContext = context; 290 mInterfaceName = ifName; 291 mClatInterfaceName = CLAT_PREFIX + ifName; 292 mCallback = callback; 293 mNwService = nwService; 294 295 mNetlinkTracker = new NetlinkTracker( 296 mInterfaceName, 297 new NetlinkTracker.Callback() { 298 @Override 299 public void update() { 300 sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED); 301 } 302 }) { 303 @Override 304 public void interfaceAdded(String iface) { 305 super.interfaceAdded(iface); 306 if (mClatInterfaceName.equals(iface)) { 307 mCallback.setNeighborDiscoveryOffload(false); 308 } 309 } 310 311 @Override 312 public void interfaceRemoved(String iface) { 313 super.interfaceRemoved(iface); 314 if (mClatInterfaceName.equals(iface)) { 315 // TODO: consider sending a message to the IpManager main 316 // StateMachine thread, in case "NDO enabled" state becomes 317 // tied to more things that 464xlat operation. 318 mCallback.setNeighborDiscoveryOffload(true); 319 } 320 } 321 }; 322 323 try { 324 mNwService.registerObserver(mNetlinkTracker); 325 } catch (RemoteException e) { 326 Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString()); 327 } 328 329 resetLinkProperties(); 330 331 // Super simple StateMachine. 332 addState(mStoppedState); 333 addState(mStartedState); 334 addState(mStoppingState); 335 336 setInitialState(mStoppedState); 337 setLogRecSize(MAX_LOG_RECORDS); 338 super.start(); 339 } 340 341 @Override 342 protected void onQuitting() { 343 mCallback.onQuit(); 344 } 345 346 // Shut down this IpManager instance altogether. 347 public void shutdown() { 348 stop(); 349 quit(); 350 } 351 352 public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() { 353 return new ProvisioningConfiguration.Builder(); 354 } 355 356 public void startProvisioning(ProvisioningConfiguration req) { 357 getNetworkInterface(); 358 sendMessage(CMD_START, new ProvisioningConfiguration(req)); 359 } 360 361 // TODO: Delete this. 362 public void startProvisioning(StaticIpConfiguration staticIpConfig) { 363 startProvisioning(buildProvisioningConfiguration() 364 .withStaticConfiguration(staticIpConfig) 365 .build()); 366 } 367 368 public void startProvisioning() { 369 startProvisioning(new ProvisioningConfiguration()); 370 } 371 372 public void stop() { 373 sendMessage(CMD_STOP); 374 } 375 376 public void confirmConfiguration() { 377 sendMessage(CMD_CONFIRM); 378 } 379 380 public void completedPreDhcpAction() { 381 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 382 } 383 384 /** 385 * Set the TCP buffer sizes to use. 386 * 387 * This may be called, repeatedly, at any time before or after a call to 388 * #startProvisioning(). The setting is cleared upon calling #stop(). 389 */ 390 public void setTcpBufferSizes(String tcpBufferSizes) { 391 sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes); 392 } 393 394 /** 395 * Set the HTTP Proxy configuration to use. 396 * 397 * This may be called, repeatedly, at any time before or after a call to 398 * #startProvisioning(). The setting is cleared upon calling #stop(). 399 */ 400 public void setHttpProxy(ProxyInfo proxyInfo) { 401 sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo); 402 } 403 404 /** 405 * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering, 406 * if not, Callback.setFallbackMulticastFilter() is called. 407 */ 408 public void setMulticastFilter(boolean enabled) { 409 sendMessage(CMD_SET_MULTICAST_FILTER, enabled); 410 } 411 412 public LinkProperties getLinkProperties() { 413 synchronized (mLock) { 414 return new LinkProperties(mLinkProperties); 415 } 416 } 417 418 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 419 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 420 pw.println("APF dump:"); 421 pw.increaseIndent(); 422 // Thread-unsafe access to mApfFilter but just used for debugging. 423 ApfFilter apfFilter = mApfFilter; 424 if (apfFilter != null) { 425 apfFilter.dump(pw); 426 } else { 427 pw.println("No apf support"); 428 } 429 pw.decreaseIndent(); 430 } 431 432 433 /** 434 * Internals. 435 */ 436 437 @Override 438 protected String getWhatToString(int what) { 439 return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what)); 440 } 441 442 @Override 443 protected String getLogRecString(Message msg) { 444 final String logLine = String.format( 445 "iface{%s/%d} arg1{%d} arg2{%d} obj{%s}", 446 mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(), 447 msg.arg1, msg.arg2, Objects.toString(msg.obj)); 448 if (VDBG) { 449 Log.d(mTag, getWhatToString(msg.what) + " " + logLine); 450 } 451 return logLine; 452 } 453 454 private void getNetworkInterface() { 455 try { 456 mNetworkInterface = NetworkInterface.getByName(mInterfaceName); 457 } catch (SocketException | NullPointerException e) { 458 // TODO: throw new IllegalStateException. 459 Log.e(mTag, "ALERT: Failed to get interface object: ", e); 460 } 461 } 462 463 // This needs to be called with care to ensure that our LinkProperties 464 // are in sync with the actual LinkProperties of the interface. For example, 465 // we should only call this if we know for sure that there are no IP addresses 466 // assigned to the interface, etc. 467 private void resetLinkProperties() { 468 mNetlinkTracker.clearLinkProperties(); 469 mConfiguration = null; 470 mDhcpResults = null; 471 mTcpBufferSizes = ""; 472 mHttpProxy = null; 473 474 synchronized (mLock) { 475 mLinkProperties = new LinkProperties(); 476 mLinkProperties.setInterfaceName(mInterfaceName); 477 } 478 } 479 480 // For now: use WifiStateMachine's historical notion of provisioned. 481 private static boolean isProvisioned(LinkProperties lp) { 482 // For historical reasons, we should connect even if all we have is 483 // an IPv4 address and nothing else. 484 return lp.isProvisioned() || lp.hasIPv4Address(); 485 } 486 487 // TODO: Investigate folding all this into the existing static function 488 // LinkProperties.compareProvisioning() or some other single function that 489 // takes two LinkProperties objects and returns a ProvisioningChange 490 // object that is a correct and complete assessment of what changed, taking 491 // account of the asymmetries described in the comments in this function. 492 // Then switch to using it everywhere (IpReachabilityMonitor, etc.). 493 private static ProvisioningChange compareProvisioning( 494 LinkProperties oldLp, LinkProperties newLp) { 495 ProvisioningChange delta; 496 497 final boolean wasProvisioned = isProvisioned(oldLp); 498 final boolean isProvisioned = isProvisioned(newLp); 499 500 if (!wasProvisioned && isProvisioned) { 501 delta = ProvisioningChange.GAINED_PROVISIONING; 502 } else if (wasProvisioned && isProvisioned) { 503 delta = ProvisioningChange.STILL_PROVISIONED; 504 } else if (!wasProvisioned && !isProvisioned) { 505 delta = ProvisioningChange.STILL_NOT_PROVISIONED; 506 } else { 507 // (wasProvisioned && !isProvisioned) 508 // 509 // Note that this is true even if we lose a configuration element 510 // (e.g., a default gateway) that would not be required to advance 511 // into provisioned state. This is intended: if we have a default 512 // router and we lose it, that's a sure sign of a problem, but if 513 // we connect to a network with no IPv4 DNS servers, we consider 514 // that to be a network without DNS servers and connect anyway. 515 // 516 // See the comment below. 517 delta = ProvisioningChange.LOST_PROVISIONING; 518 } 519 520 // Additionally: 521 // 522 // Partial configurations (e.g., only an IPv4 address with no DNS 523 // servers and no default route) are accepted as long as DHCPv4 524 // succeeds. On such a network, isProvisioned() will always return 525 // false, because the configuration is not complete, but we want to 526 // connect anyway. It might be a disconnected network such as a 527 // Chromecast or a wireless printer, for example. 528 // 529 // Because on such a network isProvisioned() will always return false, 530 // delta will never be LOST_PROVISIONING. So check for loss of 531 // provisioning here too. 532 if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) || 533 (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) { 534 delta = ProvisioningChange.LOST_PROVISIONING; 535 } 536 537 return delta; 538 } 539 540 private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) { 541 if (mApfFilter != null) mApfFilter.setLinkProperties(newLp); 542 switch (delta) { 543 case GAINED_PROVISIONING: 544 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); } 545 mCallback.onProvisioningSuccess(newLp); 546 break; 547 548 case LOST_PROVISIONING: 549 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } 550 mCallback.onProvisioningFailure(newLp); 551 break; 552 553 default: 554 if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); } 555 mCallback.onLinkPropertiesChange(newLp); 556 break; 557 } 558 } 559 560 private ProvisioningChange setLinkProperties(LinkProperties newLp) { 561 if (mIpReachabilityMonitor != null) { 562 mIpReachabilityMonitor.updateLinkProperties(newLp); 563 } 564 565 ProvisioningChange delta; 566 synchronized (mLock) { 567 delta = compareProvisioning(mLinkProperties, newLp); 568 mLinkProperties = new LinkProperties(newLp); 569 } 570 571 if (DBG) { 572 switch (delta) { 573 case GAINED_PROVISIONING: 574 case LOST_PROVISIONING: 575 Log.d(mTag, "provisioning: " + delta); 576 break; 577 } 578 } 579 580 return delta; 581 } 582 583 private boolean linkPropertiesUnchanged(LinkProperties newLp) { 584 synchronized (mLock) { 585 return Objects.equals(newLp, mLinkProperties); 586 } 587 } 588 589 private LinkProperties assembleLinkProperties() { 590 // [1] Create a new LinkProperties object to populate. 591 LinkProperties newLp = new LinkProperties(); 592 newLp.setInterfaceName(mInterfaceName); 593 594 // [2] Pull in data from netlink: 595 // - IPv4 addresses 596 // - IPv6 addresses 597 // - IPv6 routes 598 // - IPv6 DNS servers 599 LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties(); 600 newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses()); 601 for (RouteInfo route : netlinkLinkProperties.getRoutes()) { 602 newLp.addRoute(route); 603 } 604 for (InetAddress dns : netlinkLinkProperties.getDnsServers()) { 605 // Only add likely reachable DNS servers. 606 // TODO: investigate deleting this. 607 if (newLp.isReachable(dns)) { 608 newLp.addDnsServer(dns); 609 } 610 } 611 612 // [3] Add in data from DHCPv4, if available. 613 // 614 // mDhcpResults is never shared with any other owner so we don't have 615 // to worry about concurrent modification. 616 if (mDhcpResults != null) { 617 for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) { 618 newLp.addRoute(route); 619 } 620 for (InetAddress dns : mDhcpResults.dnsServers) { 621 // Only add likely reachable DNS servers. 622 // TODO: investigate deleting this. 623 if (newLp.isReachable(dns)) { 624 newLp.addDnsServer(dns); 625 } 626 } 627 newLp.setDomains(mDhcpResults.domains); 628 629 if (mDhcpResults.mtu != 0) { 630 newLp.setMtu(mDhcpResults.mtu); 631 } 632 } 633 634 // [4] Add in TCP buffer sizes and HTTP Proxy config, if available. 635 if (!TextUtils.isEmpty(mTcpBufferSizes)) { 636 newLp.setTcpBufferSizes(mTcpBufferSizes); 637 } 638 if (mHttpProxy != null) { 639 newLp.setHttpProxy(mHttpProxy); 640 } 641 642 if (VDBG) { 643 Log.d(mTag, "newLp{" + newLp + "}"); 644 } 645 return newLp; 646 } 647 648 // Returns false if we have lost provisioning, true otherwise. 649 private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) { 650 final LinkProperties newLp = assembleLinkProperties(); 651 if (linkPropertiesUnchanged(newLp)) { 652 return true; 653 } 654 final ProvisioningChange delta = setLinkProperties(newLp); 655 if (sendCallbacks) { 656 dispatchCallback(delta, newLp); 657 } 658 return (delta != ProvisioningChange.LOST_PROVISIONING); 659 } 660 661 private void clearIPv4Address() { 662 try { 663 final InterfaceConfiguration ifcg = new InterfaceConfiguration(); 664 ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0")); 665 mNwService.setInterfaceConfig(mInterfaceName, ifcg); 666 } catch (RemoteException e) { 667 Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e); 668 } 669 } 670 671 private void handleIPv4Success(DhcpResults dhcpResults) { 672 mDhcpResults = new DhcpResults(dhcpResults); 673 final LinkProperties newLp = assembleLinkProperties(); 674 final ProvisioningChange delta = setLinkProperties(newLp); 675 676 if (VDBG) { 677 Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); 678 } 679 mCallback.onNewDhcpResults(dhcpResults); 680 681 dispatchCallback(delta, newLp); 682 } 683 684 private void handleIPv4Failure() { 685 // TODO: Figure out to de-dup this and the same code in DhcpClient. 686 clearIPv4Address(); 687 mDhcpResults = null; 688 final LinkProperties newLp = assembleLinkProperties(); 689 ProvisioningChange delta = setLinkProperties(newLp); 690 // If we've gotten here and we're still not provisioned treat that as 691 // a total loss of provisioning. 692 // 693 // Either (a) static IP configuration failed or (b) DHCPv4 failed AND 694 // there was no usable IPv6 obtained before the DHCPv4 timeout. 695 // 696 // Regardless: GAME OVER. 697 // 698 // TODO: Make the DHCP client not time out and just continue in 699 // exponential backoff. Callers such as Wi-Fi which need a timeout 700 // should implement it themselves. 701 if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) { 702 delta = ProvisioningChange.LOST_PROVISIONING; 703 } 704 705 if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); } 706 mCallback.onNewDhcpResults(null); 707 708 dispatchCallback(delta, newLp); 709 if (delta == ProvisioningChange.LOST_PROVISIONING) { 710 transitionTo(mStoppingState); 711 } 712 } 713 714 class StoppedState extends State { 715 @Override 716 public void enter() { 717 try { 718 mNwService.disableIpv6(mInterfaceName); 719 mNwService.clearInterfaceAddresses(mInterfaceName); 720 } catch (Exception e) { 721 Log.e(mTag, "Failed to clear addresses or disable IPv6" + e); 722 } 723 724 resetLinkProperties(); 725 } 726 727 @Override 728 public boolean processMessage(Message msg) { 729 switch (msg.what) { 730 case CMD_STOP: 731 break; 732 733 case CMD_START: 734 mConfiguration = (ProvisioningConfiguration) msg.obj; 735 transitionTo(mStartedState); 736 break; 737 738 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 739 handleLinkPropertiesUpdate(NO_CALLBACKS); 740 break; 741 742 case CMD_UPDATE_TCP_BUFFER_SIZES: 743 mTcpBufferSizes = (String) msg.obj; 744 handleLinkPropertiesUpdate(NO_CALLBACKS); 745 break; 746 747 case CMD_UPDATE_HTTP_PROXY: 748 mHttpProxy = (ProxyInfo) msg.obj; 749 handleLinkPropertiesUpdate(NO_CALLBACKS); 750 break; 751 752 case CMD_SET_MULTICAST_FILTER: 753 mMulticastFiltering = (boolean) msg.obj; 754 break; 755 756 case DhcpClient.CMD_ON_QUIT: 757 // Everything is already stopped. 758 Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped)."); 759 break; 760 761 default: 762 return NOT_HANDLED; 763 } 764 return HANDLED; 765 } 766 } 767 768 class StoppingState extends State { 769 @Override 770 public void enter() { 771 if (mDhcpClient == null) { 772 // There's no DHCPv4 for which to wait; proceed to stopped. 773 transitionTo(mStoppedState); 774 } 775 } 776 777 @Override 778 public boolean processMessage(Message msg) { 779 switch (msg.what) { 780 case DhcpClient.CMD_ON_QUIT: 781 mDhcpClient = null; 782 transitionTo(mStoppedState); 783 break; 784 785 default: 786 deferMessage(msg); 787 } 788 return HANDLED; 789 } 790 } 791 792 class StartedState extends State { 793 @Override 794 public void enter() { 795 mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface, 796 mCallback, mMulticastFiltering); 797 // TODO: investigate the effects of any multicast filtering racing/interfering with the 798 // rest of this IP configuration startup. 799 if (mApfFilter == null) mCallback.setFallbackMulticastFilter(mMulticastFiltering); 800 // Set privacy extensions. 801 try { 802 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); 803 mNwService.enableIpv6(mInterfaceName); 804 // TODO: Perhaps clearIPv4Address() as well. 805 } catch (RemoteException re) { 806 Log.e(mTag, "Unable to change interface settings: " + re); 807 } catch (IllegalStateException ie) { 808 Log.e(mTag, "Unable to change interface settings: " + ie); 809 } 810 811 if (mConfiguration.mUsingIpReachabilityMonitor) { 812 mIpReachabilityMonitor = new IpReachabilityMonitor( 813 mContext, 814 mInterfaceName, 815 new IpReachabilityMonitor.Callback() { 816 @Override 817 public void notifyLost(InetAddress ip, String logMsg) { 818 mCallback.onReachabilityLost(logMsg); 819 } 820 }); 821 } 822 823 // If we have a StaticIpConfiguration attempt to apply it and 824 // handle the result accordingly. 825 if (mConfiguration.mStaticIpConfig != null) { 826 if (applyStaticIpConfig()) { 827 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig)); 828 } else { 829 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } 830 mCallback.onProvisioningFailure(getLinkProperties()); 831 transitionTo(mStoppingState); 832 } 833 } else { 834 // Start DHCPv4. 835 mDhcpClient = DhcpClient.makeDhcpClient( 836 mContext, 837 IpManager.this, 838 mInterfaceName); 839 mDhcpClient.registerForPreDhcpNotification(); 840 mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); 841 } 842 } 843 844 @Override 845 public void exit() { 846 if (mIpReachabilityMonitor != null) { 847 mIpReachabilityMonitor.stop(); 848 mIpReachabilityMonitor = null; 849 } 850 851 if (mDhcpClient != null) { 852 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP); 853 mDhcpClient.doQuit(); 854 } 855 856 if (mApfFilter != null) { 857 mApfFilter.shutdown(); 858 mApfFilter = null; 859 } 860 861 resetLinkProperties(); 862 } 863 864 @Override 865 public boolean processMessage(Message msg) { 866 switch (msg.what) { 867 case CMD_STOP: 868 transitionTo(mStoppedState); 869 break; 870 871 case CMD_START: 872 Log.e(mTag, "ALERT: START received in StartedState. Please fix caller."); 873 break; 874 875 case CMD_CONFIRM: 876 // TODO: Possibly introduce a second type of confirmation 877 // that both probes (a) on-link neighbors and (b) does 878 // a DHCPv4 RENEW. We used to do this on Wi-Fi framework 879 // roams. 880 if (mIpReachabilityMonitor != null) { 881 mIpReachabilityMonitor.probeAll(); 882 } 883 break; 884 885 case EVENT_PRE_DHCP_ACTION_COMPLETE: 886 // It's possible to reach here if, for example, someone 887 // calls completedPreDhcpAction() after provisioning with 888 // a static IP configuration. 889 if (mDhcpClient != null) { 890 mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE); 891 } 892 break; 893 894 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 895 if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) { 896 transitionTo(mStoppedState); 897 } 898 break; 899 900 case CMD_UPDATE_TCP_BUFFER_SIZES: 901 mTcpBufferSizes = (String) msg.obj; 902 // This cannot possibly change provisioning state. 903 handleLinkPropertiesUpdate(SEND_CALLBACKS); 904 break; 905 906 case CMD_UPDATE_HTTP_PROXY: 907 mHttpProxy = (ProxyInfo) msg.obj; 908 // This cannot possibly change provisioning state. 909 handleLinkPropertiesUpdate(SEND_CALLBACKS); 910 break; 911 912 case CMD_SET_MULTICAST_FILTER: { 913 mMulticastFiltering = (boolean) msg.obj; 914 if (mApfFilter != null) { 915 mApfFilter.setMulticastFilter(mMulticastFiltering); 916 } else { 917 mCallback.setFallbackMulticastFilter(mMulticastFiltering); 918 } 919 break; 920 } 921 922 case DhcpClient.CMD_PRE_DHCP_ACTION: 923 if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); } 924 if (mConfiguration.mRequestedPreDhcpAction) { 925 mCallback.onPreDhcpAction(); 926 } else { 927 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 928 } 929 break; 930 931 case DhcpClient.CMD_POST_DHCP_ACTION: { 932 // Note that onPostDhcpAction() is likely to be 933 // asynchronous, and thus there is no guarantee that we 934 // will be able to observe any of its effects here. 935 if (VDBG) { Log.d(mTag, "onPostDhcpAction()"); } 936 mCallback.onPostDhcpAction(); 937 938 final DhcpResults dhcpResults = (DhcpResults) msg.obj; 939 switch (msg.arg1) { 940 case DhcpClient.DHCP_SUCCESS: 941 handleIPv4Success(dhcpResults); 942 break; 943 case DhcpClient.DHCP_FAILURE: 944 handleIPv4Failure(); 945 break; 946 default: 947 Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1); 948 } 949 break; 950 } 951 952 case DhcpClient.CMD_ON_QUIT: 953 // DHCPv4 quit early for some reason. 954 Log.e(mTag, "Unexpected CMD_ON_QUIT."); 955 mDhcpClient = null; 956 break; 957 958 default: 959 return NOT_HANDLED; 960 } 961 return HANDLED; 962 } 963 964 private boolean applyStaticIpConfig() { 965 final InterfaceConfiguration ifcg = new InterfaceConfiguration(); 966 ifcg.setLinkAddress(mConfiguration.mStaticIpConfig.ipAddress); 967 ifcg.setInterfaceUp(); 968 try { 969 mNwService.setInterfaceConfig(mInterfaceName, ifcg); 970 if (DBG) Log.d(mTag, "Static IP configuration succeeded"); 971 } catch (IllegalStateException | RemoteException e) { 972 Log.e(mTag, "Static IP configuration failed: ", e); 973 return false; 974 } 975 976 return true; 977 } 978 } 979} 980