2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.net.ip;
19 import com.android.internal.util.MessageUtils;
20 import com.android.internal.util.WakeupMessage;
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.os.INetworkManagementService;
37 import android.os.Message;
38 import android.os.RemoteException;
39 import android.os.ServiceManager;
40 import android.os.SystemClock;
41 import android.text.TextUtils;
42 import android.util.LocalLog;
43 import android.util.Log;
44 import android.util.SparseArray;
46 import com.android.internal.annotations.VisibleForTesting;
47 import com.android.internal.util.IndentingPrintWriter;
48 import com.android.internal.util.State;
49 import com.android.internal.util.StateMachine;
50 import com.android.server.net.NetlinkTracker;
52 import java.io.FileDescriptor;
53 import java.io.PrintWriter;
54 import java.net.InetAddress;
55 import java.net.NetworkInterface;
56 import java.net.SocketException;
57 import java.util.Objects;
58 import java.util.StringJoiner;
64 * This class provides the interface to IP-layer provisioning and maintenance
65 * functionality that can be used by transport layers like Wi-Fi, Ethernet,
69 * IpManager is designed to be instantiated as soon as the interface name is
70 * known and can be as long-lived as the class containing it (i.e. declaring
71 * it "private final" is okay).
75 public class IpManager extends StateMachine {
76 private static final boolean DBG = false;
77 private static final boolean VDBG = false;
79 // For message logging.
80 private static final Class[] sMessageClasses = { IpManager.class, DhcpClient.class };
81 private static final SparseArray<String> sWhatToString =
82 MessageUtils.findMessageNames(sMessageClasses);
85 * Callbacks for handling IpManager events.
87 public static class Callback {
88 // In order to receive onPreDhcpAction(), call #withPreDhcpAction()
89 // when constructing a ProvisioningConfiguration.
91 // Implementations of onPreDhcpAction() must call
92 // IpManager#completedPreDhcpAction() to indicate that DHCP is clear
94 public void onPreDhcpAction() {}
95 public void onPostDhcpAction() {}
97 // This is purely advisory and not an indication of provisioning
98 // success or failure. This is only here for callers that want to
99 // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
100 // DHCPv4 or static IPv4 configuration failure or success can be
101 // determined by whether or not the passed-in DhcpResults object is
103 public void onNewDhcpResults(DhcpResults dhcpResults) {}
105 public void onProvisioningSuccess(LinkProperties newLp) {}
106 public void onProvisioningFailure(LinkProperties newLp) {}
108 // Invoked on LinkProperties changes.
109 public void onLinkPropertiesChange(LinkProperties newLp) {}
111 // Called when the internal IpReachabilityMonitor (if enabled) has
112 // detected the loss of a critical number of required neighbors.
113 public void onReachabilityLost(String logMsg) {}
115 // Called when the IpManager state machine terminates.
116 public void onQuit() {}
118 // Install an APF program to filter incoming packets.
119 public void installPacketFilter(byte[] filter) {}
121 // If multicast filtering cannot be accomplished with APF, this function will be called to
122 // actuate multicast filtering using another means.
123 public void setFallbackMulticastFilter(boolean enabled) {}
125 // Enabled/disable Neighbor Discover offload functionality. This is
126 // called, for example, whenever 464xlat is being started or stopped.
127 public void setNeighborDiscoveryOffload(boolean enable) {}
130 public static class WaitForProvisioningCallback extends Callback {
131 private LinkProperties mCallbackLinkProperties;
133 public LinkProperties waitForProvisioning() {
134 synchronized (this) {
137 } catch (InterruptedException e) {}
138 return mCallbackLinkProperties;
143 public void onProvisioningSuccess(LinkProperties newLp) {
144 synchronized (this) {
145 mCallbackLinkProperties = newLp;
151 public void onProvisioningFailure(LinkProperties newLp) {
152 synchronized (this) {
153 mCallbackLinkProperties = null;
159 // Use a wrapper class to log in order to ensure complete and detailed
160 // logging. This method is lighter weight than annotations/reflection
161 // and has the following benefits:
163 // - No invoked method can be forgotten.
164 // Any new method added to IpManager.Callback must be overridden
165 // here or it will never be called.
167 // - No invoking call site can be forgotten.
168 // Centralized logging in this way means call sites don't need to
169 // remember to log, and therefore no call site can be forgotten.
171 // - No variation in log format among call sites.
172 // Encourages logging of any available arguments, and all call sites
173 // are necessarily logged identically.
175 // TODO: Find an lighter weight approach.
176 private class LoggingCallbackWrapper extends Callback {
177 private static final String PREFIX = "INVOKE ";
178 private Callback mCallback;
180 public LoggingCallbackWrapper(Callback callback) {
181 mCallback = callback;
184 private void log(String msg) {
185 mLocalLog.log(PREFIX + msg);
189 public void onPreDhcpAction() {
190 mCallback.onPreDhcpAction();
191 log("onPreDhcpAction()");
194 public void onPostDhcpAction() {
195 mCallback.onPostDhcpAction();
196 log("onPostDhcpAction()");
199 public void onNewDhcpResults(DhcpResults dhcpResults) {
200 mCallback.onNewDhcpResults(dhcpResults);
201 log("onNewDhcpResults({" + dhcpResults + "})");
204 public void onProvisioningSuccess(LinkProperties newLp) {
205 mCallback.onProvisioningSuccess(newLp);
206 log("onProvisioningSuccess({" + newLp + "})");
209 public void onProvisioningFailure(LinkProperties newLp) {
210 mCallback.onProvisioningFailure(newLp);
211 log("onProvisioningFailure({" + newLp + "})");
214 public void onLinkPropertiesChange(LinkProperties newLp) {
215 mCallback.onLinkPropertiesChange(newLp);
216 log("onLinkPropertiesChange({" + newLp + "})");
219 public void onReachabilityLost(String logMsg) {
220 mCallback.onReachabilityLost(logMsg);
221 log("onReachabilityLost(" + logMsg + ")");
224 public void onQuit() {
229 public void installPacketFilter(byte[] filter) {
230 mCallback.installPacketFilter(filter);
231 log("installPacketFilter(byte[" + filter.length + "])");
234 public void setFallbackMulticastFilter(boolean enabled) {
235 mCallback.setFallbackMulticastFilter(enabled);
236 log("setFallbackMulticastFilter(" + enabled + ")");
239 public void setNeighborDiscoveryOffload(boolean enable) {
240 mCallback.setNeighborDiscoveryOffload(enable);
241 log("setNeighborDiscoveryOffload(" + enable + ")");
246 * This class encapsulates parameters to be passed to
247 * IpManager#startProvisioning(). A defensive copy is made by IpManager
248 * and the values specified herein are in force until IpManager#stop()
253 * final ProvisioningConfiguration config =
254 * mIpManager.buildProvisioningConfiguration()
255 * .withPreDhcpAction()
256 * .withProvisioningTimeoutMs(36 * 1000)
258 * mIpManager.startProvisioning(config);
262 * The specified provisioning configuration will only be active until
263 * IpManager#stop() is called. Future calls to IpManager#startProvisioning()
264 * must specify the configuration again.
266 public static class ProvisioningConfiguration {
267 // TODO: Delete this default timeout once those callers that care are
268 // fixed to pass in their preferred timeout.
270 // We pick 36 seconds so we can send DHCP requests at
272 // t=0, t=2, t=6, t=14, t=30
274 // allowing for 10% jitter.
275 private static final int DEFAULT_TIMEOUT_MS = 36 * 1000;
277 public static class Builder {
278 private ProvisioningConfiguration mConfig = new ProvisioningConfiguration();
280 public Builder withoutIPv4() {
281 mConfig.mEnableIPv4 = false;
285 public Builder withoutIPv6() {
286 mConfig.mEnableIPv6 = false;
290 public Builder withoutIpReachabilityMonitor() {
291 mConfig.mUsingIpReachabilityMonitor = false;
295 public Builder withPreDhcpAction() {
296 mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS;
300 public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
301 mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs;
305 public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
306 mConfig.mStaticIpConfig = staticConfig;
310 public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
311 mConfig.mApfCapabilities = apfCapabilities;
315 public Builder withProvisioningTimeoutMs(int timeoutMs) {
316 mConfig.mProvisioningTimeoutMs = timeoutMs;
320 public ProvisioningConfiguration build() {
321 return new ProvisioningConfiguration(mConfig);
325 /* package */ boolean mEnableIPv4 = true;
326 /* package */ boolean mEnableIPv6 = true;
327 /* package */ boolean mUsingIpReachabilityMonitor = true;
328 /* package */ int mRequestedPreDhcpActionMs;
329 /* package */ StaticIpConfiguration mStaticIpConfig;
330 /* package */ ApfCapabilities mApfCapabilities;
331 /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
333 public ProvisioningConfiguration() {}
335 public ProvisioningConfiguration(ProvisioningConfiguration other) {
336 mEnableIPv4 = other.mEnableIPv4;
337 mEnableIPv6 = other.mEnableIPv6;
338 mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
339 mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
340 mStaticIpConfig = other.mStaticIpConfig;
341 mApfCapabilities = other.mApfCapabilities;
342 mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
346 public String toString() {
347 return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
348 .add("mEnableIPv4: " + mEnableIPv4)
349 .add("mEnableIPv6: " + mEnableIPv6)
350 .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
351 .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
352 .add("mStaticIpConfig: " + mStaticIpConfig)
353 .add("mApfCapabilities: " + mApfCapabilities)
354 .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
359 public static final String DUMP_ARG = "ipmanager";
361 private static final int CMD_STOP = 1;
362 private static final int CMD_START = 2;
363 private static final int CMD_CONFIRM = 3;
364 private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 4;
365 // Sent by NetlinkTracker to communicate netlink events.
366 private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5;
367 private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6;
368 private static final int CMD_UPDATE_HTTP_PROXY = 7;
369 private static final int CMD_SET_MULTICAST_FILTER = 8;
370 private static final int EVENT_PROVISIONING_TIMEOUT = 9;
371 private static final int EVENT_DHCPACTION_TIMEOUT = 10;
373 private static final int MAX_LOG_RECORDS = 500;
375 private static final boolean NO_CALLBACKS = false;
376 private static final boolean SEND_CALLBACKS = true;
378 // This must match the interface prefix in clatd.c.
379 // TODO: Revert this hack once IpManager and Nat464Xlat work in concert.
380 private static final String CLAT_PREFIX = "v4-";
382 private final State mStoppedState = new StoppedState();
383 private final State mStoppingState = new StoppingState();
384 private final State mStartedState = new StartedState();
386 private final String mTag;
387 private final Context mContext;
388 private final String mInterfaceName;
389 private final String mClatInterfaceName;
391 protected final Callback mCallback;
392 private final INetworkManagementService mNwService;
393 private final NetlinkTracker mNetlinkTracker;
394 private final WakeupMessage mProvisioningTimeoutAlarm;
395 private final WakeupMessage mDhcpActionTimeoutAlarm;
396 private final LocalLog mLocalLog;
397 private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
399 private NetworkInterface mNetworkInterface;
402 * Non-final member variables accessed only from within our StateMachine.
404 private LinkProperties mLinkProperties;
405 private ProvisioningConfiguration mConfiguration;
406 private IpReachabilityMonitor mIpReachabilityMonitor;
407 private DhcpClient mDhcpClient;
408 private DhcpResults mDhcpResults;
409 private String mTcpBufferSizes;
410 private ProxyInfo mHttpProxy;
411 private ApfFilter mApfFilter;
412 private boolean mMulticastFiltering;
413 private long mStartTimeMillis;
415 public IpManager(Context context, String ifName, Callback callback)
416 throws IllegalArgumentException {
417 this(context, ifName, callback, INetworkManagementService.Stub.asInterface(
418 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)));
422 * An expanded constructor, useful for dependency injection.
424 public IpManager(Context context, String ifName, Callback callback,
425 INetworkManagementService nwService) throws IllegalArgumentException {
426 super(IpManager.class.getSimpleName() + "." + ifName);
430 mInterfaceName = ifName;
431 mClatInterfaceName = CLAT_PREFIX + ifName;
432 mCallback = new LoggingCallbackWrapper(callback);
433 mNwService = nwService;
435 mNetlinkTracker = new NetlinkTracker(
437 new NetlinkTracker.Callback() {
439 public void update() {
440 sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
444 public void interfaceAdded(String iface) {
445 super.interfaceAdded(iface);
446 if (mClatInterfaceName.equals(iface)) {
447 mCallback.setNeighborDiscoveryOffload(false);
452 public void interfaceRemoved(String iface) {
453 super.interfaceRemoved(iface);
454 if (mClatInterfaceName.equals(iface)) {
455 // TODO: consider sending a message to the IpManager main
456 // StateMachine thread, in case "NDO enabled" state becomes
457 // tied to more things that 464xlat operation.
458 mCallback.setNeighborDiscoveryOffload(true);
464 mNwService.registerObserver(mNetlinkTracker);
465 } catch (RemoteException e) {
466 Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString());
469 resetLinkProperties();
471 mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
472 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
473 mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
474 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
476 // Super simple StateMachine.
477 addState(mStoppedState);
478 addState(mStartedState);
479 addState(mStoppingState);
481 setInitialState(mStoppedState);
482 mLocalLog = new LocalLog(MAX_LOG_RECORDS);
487 protected void onQuitting() {
491 // Shut down this IpManager instance altogether.
492 public void shutdown() {
497 public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
498 return new ProvisioningConfiguration.Builder();
501 public void startProvisioning(ProvisioningConfiguration req) {
502 getNetworkInterface();
504 mCallback.setNeighborDiscoveryOffload(true);
505 sendMessage(CMD_START, new ProvisioningConfiguration(req));
508 // TODO: Delete this.
509 public void startProvisioning(StaticIpConfiguration staticIpConfig) {
510 startProvisioning(buildProvisioningConfiguration()
511 .withStaticConfiguration(staticIpConfig)
515 public void startProvisioning() {
516 startProvisioning(new ProvisioningConfiguration());
520 sendMessage(CMD_STOP);
523 public void confirmConfiguration() {
524 sendMessage(CMD_CONFIRM);
527 public void completedPreDhcpAction() {
528 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
532 * Set the TCP buffer sizes to use.
534 * This may be called, repeatedly, at any time before or after a call to
535 * #startProvisioning(). The setting is cleared upon calling #stop().
537 public void setTcpBufferSizes(String tcpBufferSizes) {
538 sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
542 * Set the HTTP Proxy configuration to use.
544 * This may be called, repeatedly, at any time before or after a call to
545 * #startProvisioning(). The setting is cleared upon calling #stop().
547 public void setHttpProxy(ProxyInfo proxyInfo) {
548 sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
552 * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering,
553 * if not, Callback.setFallbackMulticastFilter() is called.
555 public void setMulticastFilter(boolean enabled) {
556 sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
559 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
560 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
561 pw.println("APF dump:");
563 // Thread-unsafe access to mApfFilter but just used for debugging.
564 ApfFilter apfFilter = mApfFilter;
565 if (apfFilter != null) {
568 pw.println("No apf support");
573 pw.println("StateMachine dump:");
575 mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
585 protected String getWhatToString(int what) {
586 return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
590 protected String getLogRecString(Message msg) {
591 final String logLine = String.format(
593 mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(),
594 msg.arg1, msg.arg2, Objects.toString(msg.obj));
596 final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
597 mLocalLog.log(richerLogLine);
599 Log.d(mTag, richerLogLine);
606 protected boolean recordLogRec(Message msg) {
607 // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
608 // and we already log any LinkProperties change that results in an
609 // invocation of IpManager.Callback#onLinkPropertiesChange().
610 return (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
613 private void getNetworkInterface() {
615 mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
616 } catch (SocketException | NullPointerException e) {
617 // TODO: throw new IllegalStateException.
618 Log.e(mTag, "ALERT: Failed to get interface object: ", e);
622 // This needs to be called with care to ensure that our LinkProperties
623 // are in sync with the actual LinkProperties of the interface. For example,
624 // we should only call this if we know for sure that there are no IP addresses
625 // assigned to the interface, etc.
626 private void resetLinkProperties() {
627 mNetlinkTracker.clearLinkProperties();
628 mConfiguration = null;
630 mTcpBufferSizes = "";
633 mLinkProperties = new LinkProperties();
634 mLinkProperties.setInterfaceName(mInterfaceName);
637 private void recordMetric(final int type) {
638 if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); }
639 final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis;
640 mMetricsLog.log(new IpManagerEvent(mInterfaceName, type, duration));
643 // For now: use WifiStateMachine's historical notion of provisioned.
644 private static boolean isProvisioned(LinkProperties lp) {
645 // For historical reasons, we should connect even if all we have is
646 // an IPv4 address and nothing else.
647 return lp.isProvisioned() || lp.hasIPv4Address();
650 // TODO: Investigate folding all this into the existing static function
651 // LinkProperties.compareProvisioning() or some other single function that
652 // takes two LinkProperties objects and returns a ProvisioningChange
653 // object that is a correct and complete assessment of what changed, taking
654 // account of the asymmetries described in the comments in this function.
655 // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
656 private static ProvisioningChange compareProvisioning(
657 LinkProperties oldLp, LinkProperties newLp) {
658 ProvisioningChange delta;
660 final boolean wasProvisioned = isProvisioned(oldLp);
661 final boolean isProvisioned = isProvisioned(newLp);
663 if (!wasProvisioned && isProvisioned) {
664 delta = ProvisioningChange.GAINED_PROVISIONING;
665 } else if (wasProvisioned && isProvisioned) {
666 delta = ProvisioningChange.STILL_PROVISIONED;
667 } else if (!wasProvisioned && !isProvisioned) {
668 delta = ProvisioningChange.STILL_NOT_PROVISIONED;
670 // (wasProvisioned && !isProvisioned)
672 // Note that this is true even if we lose a configuration element
673 // (e.g., a default gateway) that would not be required to advance
674 // into provisioned state. This is intended: if we have a default
675 // router and we lose it, that's a sure sign of a problem, but if
676 // we connect to a network with no IPv4 DNS servers, we consider
677 // that to be a network without DNS servers and connect anyway.
679 // See the comment below.
680 delta = ProvisioningChange.LOST_PROVISIONING;
685 // Partial configurations (e.g., only an IPv4 address with no DNS
686 // servers and no default route) are accepted as long as DHCPv4
687 // succeeds. On such a network, isProvisioned() will always return
688 // false, because the configuration is not complete, but we want to
689 // connect anyway. It might be a disconnected network such as a
690 // Chromecast or a wireless printer, for example.
692 // Because on such a network isProvisioned() will always return false,
693 // delta will never be LOST_PROVISIONING. So check for loss of
694 // provisioning here too.
695 if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) ||
696 (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) {
697 delta = ProvisioningChange.LOST_PROVISIONING;
702 // If the previous link properties had a global IPv6 address and an
703 // IPv6 default route then also consider the loss of that default route
704 // to be a loss of provisioning. See b/27962810.
705 if (oldLp.hasGlobalIPv6Address() && oldLp.hasIPv6DefaultRoute() &&
706 !newLp.hasIPv6DefaultRoute()) {
707 delta = ProvisioningChange.LOST_PROVISIONING;
713 private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
715 case GAINED_PROVISIONING:
716 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
717 recordMetric(IpManagerEvent.PROVISIONING_OK);
718 mCallback.onProvisioningSuccess(newLp);
721 case LOST_PROVISIONING:
722 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
723 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
724 mCallback.onProvisioningFailure(newLp);
728 if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
729 mCallback.onLinkPropertiesChange(newLp);
734 // Updates all IpManager-related state concerned with LinkProperties.
735 // Returns a ProvisioningChange for possibly notifying other interested
736 // parties that are not fronted by IpManager.
737 private ProvisioningChange setLinkProperties(LinkProperties newLp) {
738 if (mApfFilter != null) {
739 mApfFilter.setLinkProperties(newLp);
741 if (mIpReachabilityMonitor != null) {
742 mIpReachabilityMonitor.updateLinkProperties(newLp);
745 ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
746 mLinkProperties = new LinkProperties(newLp);
748 if (delta == ProvisioningChange.GAINED_PROVISIONING) {
749 // TODO: Add a proper ProvisionedState and cancel the alarm in
750 // its enter() method.
751 mProvisioningTimeoutAlarm.cancel();
757 private boolean linkPropertiesUnchanged(LinkProperties newLp) {
758 return Objects.equals(newLp, mLinkProperties);
761 private LinkProperties assembleLinkProperties() {
762 // [1] Create a new LinkProperties object to populate.
763 LinkProperties newLp = new LinkProperties();
764 newLp.setInterfaceName(mInterfaceName);
766 // [2] Pull in data from netlink:
770 // - IPv6 DNS servers
771 LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
772 newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
773 for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
774 newLp.addRoute(route);
776 for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
777 // Only add likely reachable DNS servers.
778 // TODO: investigate deleting this.
779 if (newLp.isReachable(dns)) {
780 newLp.addDnsServer(dns);
784 // [3] Add in data from DHCPv4, if available.
786 // mDhcpResults is never shared with any other owner so we don't have
787 // to worry about concurrent modification.
788 if (mDhcpResults != null) {
789 for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
790 newLp.addRoute(route);
792 for (InetAddress dns : mDhcpResults.dnsServers) {
793 // Only add likely reachable DNS servers.
794 // TODO: investigate deleting this.
795 if (newLp.isReachable(dns)) {
796 newLp.addDnsServer(dns);
799 newLp.setDomains(mDhcpResults.domains);
801 if (mDhcpResults.mtu != 0) {
802 newLp.setMtu(mDhcpResults.mtu);
806 // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
807 if (!TextUtils.isEmpty(mTcpBufferSizes)) {
808 newLp.setTcpBufferSizes(mTcpBufferSizes);
810 if (mHttpProxy != null) {
811 newLp.setHttpProxy(mHttpProxy);
815 Log.d(mTag, "newLp{" + newLp + "}");
820 // Returns false if we have lost provisioning, true otherwise.
821 private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
822 final LinkProperties newLp = assembleLinkProperties();
823 if (linkPropertiesUnchanged(newLp)) {
826 final ProvisioningChange delta = setLinkProperties(newLp);
828 dispatchCallback(delta, newLp);
830 return (delta != ProvisioningChange.LOST_PROVISIONING);
833 private boolean setIPv4Address(LinkAddress address) {
834 final InterfaceConfiguration ifcg = new InterfaceConfiguration();
835 ifcg.setLinkAddress(address);
837 mNwService.setInterfaceConfig(mInterfaceName, ifcg);
838 if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
839 } catch (IllegalStateException | RemoteException e) {
840 Log.e(mTag, "IPv4 configuration failed: ", e);
846 private void clearIPv4Address() {
848 final InterfaceConfiguration ifcg = new InterfaceConfiguration();
849 ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
850 mNwService.setInterfaceConfig(mInterfaceName, ifcg);
851 } catch (IllegalStateException | RemoteException e) {
852 Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
856 private void handleIPv4Success(DhcpResults dhcpResults) {
857 mDhcpResults = new DhcpResults(dhcpResults);
858 final LinkProperties newLp = assembleLinkProperties();
859 final ProvisioningChange delta = setLinkProperties(newLp);
862 Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
864 mCallback.onNewDhcpResults(dhcpResults);
865 dispatchCallback(delta, newLp);
868 private void handleIPv4Failure() {
869 // TODO: Investigate deleting this clearIPv4Address() call.
871 // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
872 // that could trigger a call to this function. If we missed handling
873 // that message in StartedState for some reason we would still clear
874 // any addresses upon entry to StoppedState.
877 if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
878 mCallback.onNewDhcpResults(null);
880 handleProvisioningFailure();
883 private void handleProvisioningFailure() {
884 final LinkProperties newLp = assembleLinkProperties();
885 ProvisioningChange delta = setLinkProperties(newLp);
886 // If we've gotten here and we're still not provisioned treat that as
887 // a total loss of provisioning.
889 // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
890 // there was no usable IPv6 obtained before a non-zero provisioning
893 // Regardless: GAME OVER.
894 if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
895 delta = ProvisioningChange.LOST_PROVISIONING;
898 dispatchCallback(delta, newLp);
899 if (delta == ProvisioningChange.LOST_PROVISIONING) {
900 transitionTo(mStoppingState);
904 private boolean startIPv4() {
905 // If we have a StaticIpConfiguration attempt to apply it and
906 // handle the result accordingly.
907 if (mConfiguration.mStaticIpConfig != null) {
908 if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
909 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
911 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
912 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
913 mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
918 mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName);
919 mDhcpClient.registerForPreDhcpNotification();
920 mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
926 private boolean startIPv6() {
927 // Set privacy extensions.
929 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
930 mNwService.enableIpv6(mInterfaceName);
931 } catch (RemoteException re) {
932 Log.e(mTag, "Unable to change interface settings: " + re);
934 } catch (IllegalStateException ie) {
935 Log.e(mTag, "Unable to change interface settings: " + ie);
943 class StoppedState extends State {
945 public void enter() {
947 mNwService.disableIpv6(mInterfaceName);
948 mNwService.clearInterfaceAddresses(mInterfaceName);
949 } catch (Exception e) {
950 Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
953 resetLinkProperties();
954 if (mStartTimeMillis > 0) {
955 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
956 mStartTimeMillis = 0;
961 public boolean processMessage(Message msg) {
967 mConfiguration = (ProvisioningConfiguration) msg.obj;
968 transitionTo(mStartedState);
971 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
972 handleLinkPropertiesUpdate(NO_CALLBACKS);
975 case CMD_UPDATE_TCP_BUFFER_SIZES:
976 mTcpBufferSizes = (String) msg.obj;
977 handleLinkPropertiesUpdate(NO_CALLBACKS);
980 case CMD_UPDATE_HTTP_PROXY:
981 mHttpProxy = (ProxyInfo) msg.obj;
982 handleLinkPropertiesUpdate(NO_CALLBACKS);
985 case CMD_SET_MULTICAST_FILTER:
986 mMulticastFiltering = (boolean) msg.obj;
989 case DhcpClient.CMD_ON_QUIT:
990 // Everything is already stopped.
991 Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
1001 class StoppingState extends State {
1003 public void enter() {
1004 if (mDhcpClient == null) {
1005 // There's no DHCPv4 for which to wait; proceed to stopped.
1006 transitionTo(mStoppedState);
1011 public boolean processMessage(Message msg) {
1013 case DhcpClient.CMD_ON_QUIT:
1015 transitionTo(mStoppedState);
1025 class StartedState extends State {
1026 private boolean mDhcpActionInFlight;
1029 public void enter() {
1030 mStartTimeMillis = SystemClock.elapsedRealtime();
1032 mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
1033 mCallback, mMulticastFiltering);
1034 // TODO: investigate the effects of any multicast filtering racing/interfering with the
1035 // rest of this IP configuration startup.
1036 if (mApfFilter == null) {
1037 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
1040 if (mConfiguration.mProvisioningTimeoutMs > 0) {
1041 final long alarmTime = SystemClock.elapsedRealtime() +
1042 mConfiguration.mProvisioningTimeoutMs;
1043 mProvisioningTimeoutAlarm.schedule(alarmTime);
1046 if (mConfiguration.mEnableIPv6) {
1047 // TODO: Consider transitionTo(mStoppingState) if this fails.
1051 if (mConfiguration.mEnableIPv4) {
1053 transitionTo(mStoppingState);
1058 if (mConfiguration.mUsingIpReachabilityMonitor) {
1059 mIpReachabilityMonitor = new IpReachabilityMonitor(
1062 new IpReachabilityMonitor.Callback() {
1064 public void notifyLost(InetAddress ip, String logMsg) {
1065 mCallback.onReachabilityLost(logMsg);
1072 public void exit() {
1073 mProvisioningTimeoutAlarm.cancel();
1076 if (mIpReachabilityMonitor != null) {
1077 mIpReachabilityMonitor.stop();
1078 mIpReachabilityMonitor = null;
1081 if (mDhcpClient != null) {
1082 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
1083 mDhcpClient.doQuit();
1086 if (mApfFilter != null) {
1087 mApfFilter.shutdown();
1091 resetLinkProperties();
1094 private void ensureDhcpAction() {
1095 if (!mDhcpActionInFlight) {
1096 mCallback.onPreDhcpAction();
1097 mDhcpActionInFlight = true;
1098 final long alarmTime = SystemClock.elapsedRealtime() +
1099 mConfiguration.mRequestedPreDhcpActionMs;
1100 mDhcpActionTimeoutAlarm.schedule(alarmTime);
1104 private void stopDhcpAction() {
1105 mDhcpActionTimeoutAlarm.cancel();
1106 if (mDhcpActionInFlight) {
1107 mCallback.onPostDhcpAction();
1108 mDhcpActionInFlight = false;
1113 public boolean processMessage(Message msg) {
1116 transitionTo(mStoppingState);
1120 Log.e(mTag, "ALERT: START received in StartedState. Please fix caller.");
1124 // TODO: Possibly introduce a second type of confirmation
1125 // that both probes (a) on-link neighbors and (b) does
1126 // a DHCPv4 RENEW. We used to do this on Wi-Fi framework
1128 if (mIpReachabilityMonitor != null) {
1129 mIpReachabilityMonitor.probeAll();
1133 case EVENT_PRE_DHCP_ACTION_COMPLETE:
1134 // It's possible to reach here if, for example, someone
1135 // calls completedPreDhcpAction() after provisioning with
1136 // a static IP configuration.
1137 if (mDhcpClient != null) {
1138 mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
1142 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
1143 if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
1144 transitionTo(mStoppingState);
1148 case CMD_UPDATE_TCP_BUFFER_SIZES:
1149 mTcpBufferSizes = (String) msg.obj;
1150 // This cannot possibly change provisioning state.
1151 handleLinkPropertiesUpdate(SEND_CALLBACKS);
1154 case CMD_UPDATE_HTTP_PROXY:
1155 mHttpProxy = (ProxyInfo) msg.obj;
1156 // This cannot possibly change provisioning state.
1157 handleLinkPropertiesUpdate(SEND_CALLBACKS);
1160 case CMD_SET_MULTICAST_FILTER: {
1161 mMulticastFiltering = (boolean) msg.obj;
1162 if (mApfFilter != null) {
1163 mApfFilter.setMulticastFilter(mMulticastFiltering);
1165 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
1170 case EVENT_PROVISIONING_TIMEOUT:
1171 handleProvisioningFailure();
1174 case EVENT_DHCPACTION_TIMEOUT:
1178 case DhcpClient.CMD_PRE_DHCP_ACTION:
1179 if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
1182 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
1186 case DhcpClient.CMD_CLEAR_LINKADDRESS:
1190 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
1191 final LinkAddress ipAddress = (LinkAddress) msg.obj;
1192 if (setIPv4Address(ipAddress)) {
1193 mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
1195 Log.e(mTag, "Failed to set IPv4 address!");
1196 dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
1197 new LinkProperties(mLinkProperties));
1198 transitionTo(mStoppingState);
1203 // This message is only received when:
1205 // a) initial address acquisition succeeds,
1206 // b) renew succeeds or is NAK'd,
1207 // c) rebind succeeds or is NAK'd, or
1208 // c) the lease expires,
1210 // but never when initial address acquisition fails. The latter
1211 // condition is now governed by the provisioning timeout.
1212 case DhcpClient.CMD_POST_DHCP_ACTION:
1216 case DhcpClient.DHCP_SUCCESS:
1217 handleIPv4Success((DhcpResults) msg.obj);
1219 case DhcpClient.DHCP_FAILURE:
1220 handleIPv4Failure();
1223 Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
1227 case DhcpClient.CMD_ON_QUIT:
1228 // DHCPv4 quit early for some reason.
1229 Log.e(mTag, "Unexpected CMD_ON_QUIT.");