OSDN Git Service

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