OSDN Git Service

Slightly simplify IpManager#compareProvisioning.
[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         final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned();
688         final boolean lostIPv4Address = oldLp.hasIPv4Address() && !newLp.hasIPv4Address();
689         final boolean lostIPv6Router = oldLp.hasIPv6DefaultRoute() && !newLp.hasIPv6DefaultRoute();
690
691         // Additionally:
692         //
693         // Partial configurations (e.g., only an IPv4 address with no DNS
694         // servers and no default route) are accepted as long as DHCPv4
695         // succeeds. On such a network, isProvisioned() will always return
696         // false, because the configuration is not complete, but we want to
697         // connect anyway. It might be a disconnected network such as a
698         // Chromecast or a wireless printer, for example.
699         //
700         // Because on such a network isProvisioned() will always return false,
701         // delta will never be LOST_PROVISIONING. So check for loss of
702         // provisioning here too.
703         if (lostIPv4Address || lostIPv6) {
704             delta = ProvisioningChange.LOST_PROVISIONING;
705         }
706
707         // Additionally:
708         //
709         // If the previous link properties had a global IPv6 address and an
710         // IPv6 default route then also consider the loss of that default route
711         // to be a loss of provisioning. See b/27962810.
712         if (oldLp.hasGlobalIPv6Address() && lostIPv6Router) {
713             delta = ProvisioningChange.LOST_PROVISIONING;
714         }
715
716         return delta;
717     }
718
719     private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
720         switch (delta) {
721             case GAINED_PROVISIONING:
722                 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
723                 recordMetric(IpManagerEvent.PROVISIONING_OK);
724                 mCallback.onProvisioningSuccess(newLp);
725                 break;
726
727             case LOST_PROVISIONING:
728                 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
729                 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
730                 mCallback.onProvisioningFailure(newLp);
731                 break;
732
733             default:
734                 if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
735                 mCallback.onLinkPropertiesChange(newLp);
736                 break;
737         }
738     }
739
740     // Updates all IpManager-related state concerned with LinkProperties.
741     // Returns a ProvisioningChange for possibly notifying other interested
742     // parties that are not fronted by IpManager.
743     private ProvisioningChange setLinkProperties(LinkProperties newLp) {
744         if (mApfFilter != null) {
745             mApfFilter.setLinkProperties(newLp);
746         }
747         if (mIpReachabilityMonitor != null) {
748             mIpReachabilityMonitor.updateLinkProperties(newLp);
749         }
750
751         ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
752         mLinkProperties = new LinkProperties(newLp);
753
754         if (delta == ProvisioningChange.GAINED_PROVISIONING) {
755             // TODO: Add a proper ProvisionedState and cancel the alarm in
756             // its enter() method.
757             mProvisioningTimeoutAlarm.cancel();
758         }
759
760         return delta;
761     }
762
763     private boolean linkPropertiesUnchanged(LinkProperties newLp) {
764         return Objects.equals(newLp, mLinkProperties);
765     }
766
767     private LinkProperties assembleLinkProperties() {
768         // [1] Create a new LinkProperties object to populate.
769         LinkProperties newLp = new LinkProperties();
770         newLp.setInterfaceName(mInterfaceName);
771
772         // [2] Pull in data from netlink:
773         //         - IPv4 addresses
774         //         - IPv6 addresses
775         //         - IPv6 routes
776         //         - IPv6 DNS servers
777         LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
778         newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
779         for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
780             newLp.addRoute(route);
781         }
782         for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
783             // Only add likely reachable DNS servers.
784             // TODO: investigate deleting this.
785             if (newLp.isReachable(dns)) {
786                 newLp.addDnsServer(dns);
787             }
788         }
789
790         // [3] Add in data from DHCPv4, if available.
791         //
792         // mDhcpResults is never shared with any other owner so we don't have
793         // to worry about concurrent modification.
794         if (mDhcpResults != null) {
795             for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
796                 newLp.addRoute(route);
797             }
798             for (InetAddress dns : mDhcpResults.dnsServers) {
799                 // Only add likely reachable DNS servers.
800                 // TODO: investigate deleting this.
801                 if (newLp.isReachable(dns)) {
802                     newLp.addDnsServer(dns);
803                 }
804             }
805             newLp.setDomains(mDhcpResults.domains);
806
807             if (mDhcpResults.mtu != 0) {
808                 newLp.setMtu(mDhcpResults.mtu);
809             }
810         }
811
812         // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
813         if (!TextUtils.isEmpty(mTcpBufferSizes)) {
814             newLp.setTcpBufferSizes(mTcpBufferSizes);
815         }
816         if (mHttpProxy != null) {
817             newLp.setHttpProxy(mHttpProxy);
818         }
819
820         if (VDBG) {
821             Log.d(mTag, "newLp{" + newLp + "}");
822         }
823         return newLp;
824     }
825
826     // Returns false if we have lost provisioning, true otherwise.
827     private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
828         final LinkProperties newLp = assembleLinkProperties();
829         if (linkPropertiesUnchanged(newLp)) {
830             return true;
831         }
832         final ProvisioningChange delta = setLinkProperties(newLp);
833         if (sendCallbacks) {
834             dispatchCallback(delta, newLp);
835         }
836         return (delta != ProvisioningChange.LOST_PROVISIONING);
837     }
838
839     private boolean setIPv4Address(LinkAddress address) {
840         final InterfaceConfiguration ifcg = new InterfaceConfiguration();
841         ifcg.setLinkAddress(address);
842         try {
843             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
844             if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
845         } catch (IllegalStateException | RemoteException e) {
846             Log.e(mTag, "IPv4 configuration failed: ", e);
847             return false;
848         }
849         return true;
850     }
851
852     private void clearIPv4Address() {
853         try {
854             final InterfaceConfiguration ifcg = new InterfaceConfiguration();
855             ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
856             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
857         } catch (IllegalStateException | RemoteException e) {
858             Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
859         }
860     }
861
862     private void handleIPv4Success(DhcpResults dhcpResults) {
863         mDhcpResults = new DhcpResults(dhcpResults);
864         final LinkProperties newLp = assembleLinkProperties();
865         final ProvisioningChange delta = setLinkProperties(newLp);
866
867         if (VDBG) {
868             Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
869         }
870         mCallback.onNewDhcpResults(dhcpResults);
871         dispatchCallback(delta, newLp);
872     }
873
874     private void handleIPv4Failure() {
875         // TODO: Investigate deleting this clearIPv4Address() call.
876         //
877         // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
878         // that could trigger a call to this function. If we missed handling
879         // that message in StartedState for some reason we would still clear
880         // any addresses upon entry to StoppedState.
881         clearIPv4Address();
882         mDhcpResults = null;
883         if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
884         mCallback.onNewDhcpResults(null);
885
886         handleProvisioningFailure();
887     }
888
889     private void handleProvisioningFailure() {
890         final LinkProperties newLp = assembleLinkProperties();
891         ProvisioningChange delta = setLinkProperties(newLp);
892         // If we've gotten here and we're still not provisioned treat that as
893         // a total loss of provisioning.
894         //
895         // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
896         // there was no usable IPv6 obtained before a non-zero provisioning
897         // timeout expired.
898         //
899         // Regardless: GAME OVER.
900         if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
901             delta = ProvisioningChange.LOST_PROVISIONING;
902         }
903
904         dispatchCallback(delta, newLp);
905         if (delta == ProvisioningChange.LOST_PROVISIONING) {
906             transitionTo(mStoppingState);
907         }
908     }
909
910     private boolean startIPv4() {
911         // If we have a StaticIpConfiguration attempt to apply it and
912         // handle the result accordingly.
913         if (mConfiguration.mStaticIpConfig != null) {
914             if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
915                 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
916             } else {
917                 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
918                 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
919                 mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
920                 return false;
921             }
922         } else {
923             // Start DHCPv4.
924             mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName);
925             mDhcpClient.registerForPreDhcpNotification();
926             mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
927         }
928
929         return true;
930     }
931
932     private boolean startIPv6() {
933         // Set privacy extensions.
934         try {
935             mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
936             mNwService.enableIpv6(mInterfaceName);
937         } catch (RemoteException re) {
938             Log.e(mTag, "Unable to change interface settings: " + re);
939             return false;
940         } catch (IllegalStateException ie) {
941             Log.e(mTag, "Unable to change interface settings: " + ie);
942             return false;
943         }
944
945         return true;
946     }
947
948
949     class StoppedState extends State {
950         @Override
951         public void enter() {
952             try {
953                 mNwService.disableIpv6(mInterfaceName);
954                 mNwService.clearInterfaceAddresses(mInterfaceName);
955             } catch (Exception e) {
956                 Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
957             }
958
959             resetLinkProperties();
960             if (mStartTimeMillis > 0) {
961                 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
962                 mStartTimeMillis = 0;
963             }
964         }
965
966         @Override
967         public boolean processMessage(Message msg) {
968             switch (msg.what) {
969                 case CMD_STOP:
970                     break;
971
972                 case CMD_START:
973                     mConfiguration = (ProvisioningConfiguration) msg.obj;
974                     transitionTo(mStartedState);
975                     break;
976
977                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
978                     handleLinkPropertiesUpdate(NO_CALLBACKS);
979                     break;
980
981                 case CMD_UPDATE_TCP_BUFFER_SIZES:
982                     mTcpBufferSizes = (String) msg.obj;
983                     handleLinkPropertiesUpdate(NO_CALLBACKS);
984                     break;
985
986                 case CMD_UPDATE_HTTP_PROXY:
987                     mHttpProxy = (ProxyInfo) msg.obj;
988                     handleLinkPropertiesUpdate(NO_CALLBACKS);
989                     break;
990
991                 case CMD_SET_MULTICAST_FILTER:
992                     mMulticastFiltering = (boolean) msg.obj;
993                     break;
994
995                 case DhcpClient.CMD_ON_QUIT:
996                     // Everything is already stopped.
997                     Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
998                     break;
999
1000                 default:
1001                     return NOT_HANDLED;
1002             }
1003             return HANDLED;
1004         }
1005     }
1006
1007     class StoppingState extends State {
1008         @Override
1009         public void enter() {
1010             if (mDhcpClient == null) {
1011                 // There's no DHCPv4 for which to wait; proceed to stopped.
1012                 transitionTo(mStoppedState);
1013             }
1014         }
1015
1016         @Override
1017         public boolean processMessage(Message msg) {
1018             switch (msg.what) {
1019                 case DhcpClient.CMD_ON_QUIT:
1020                     mDhcpClient = null;
1021                     transitionTo(mStoppedState);
1022                     break;
1023
1024                 default:
1025                     deferMessage(msg);
1026             }
1027             return HANDLED;
1028         }
1029     }
1030
1031     class StartedState extends State {
1032         private boolean mDhcpActionInFlight;
1033
1034         @Override
1035         public void enter() {
1036             mStartTimeMillis = SystemClock.elapsedRealtime();
1037
1038             mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
1039                     mCallback, mMulticastFiltering);
1040             // TODO: investigate the effects of any multicast filtering racing/interfering with the
1041             // rest of this IP configuration startup.
1042             if (mApfFilter == null) {
1043                 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
1044             }
1045
1046             if (mConfiguration.mProvisioningTimeoutMs > 0) {
1047                 final long alarmTime = SystemClock.elapsedRealtime() +
1048                         mConfiguration.mProvisioningTimeoutMs;
1049                 mProvisioningTimeoutAlarm.schedule(alarmTime);
1050             }
1051
1052             if (mConfiguration.mEnableIPv6) {
1053                 // TODO: Consider transitionTo(mStoppingState) if this fails.
1054                 startIPv6();
1055             }
1056
1057             if (mConfiguration.mEnableIPv4) {
1058                 if (!startIPv4()) {
1059                     transitionTo(mStoppingState);
1060                     return;
1061                 }
1062             }
1063
1064             if (mConfiguration.mUsingIpReachabilityMonitor) {
1065                 mIpReachabilityMonitor = new IpReachabilityMonitor(
1066                         mContext,
1067                         mInterfaceName,
1068                         new IpReachabilityMonitor.Callback() {
1069                             @Override
1070                             public void notifyLost(InetAddress ip, String logMsg) {
1071                                 mCallback.onReachabilityLost(logMsg);
1072                             }
1073                         },
1074                         mAvoidBadWifiTracker);
1075             }
1076         }
1077
1078         @Override
1079         public void exit() {
1080             mProvisioningTimeoutAlarm.cancel();
1081             stopDhcpAction();
1082
1083             if (mIpReachabilityMonitor != null) {
1084                 mIpReachabilityMonitor.stop();
1085                 mIpReachabilityMonitor = null;
1086             }
1087
1088             if (mDhcpClient != null) {
1089                 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
1090                 mDhcpClient.doQuit();
1091             }
1092
1093             if (mApfFilter != null) {
1094                 mApfFilter.shutdown();
1095                 mApfFilter = null;
1096             }
1097
1098             resetLinkProperties();
1099         }
1100
1101         private void ensureDhcpAction() {
1102             if (!mDhcpActionInFlight) {
1103                 mCallback.onPreDhcpAction();
1104                 mDhcpActionInFlight = true;
1105                 final long alarmTime = SystemClock.elapsedRealtime() +
1106                         mConfiguration.mRequestedPreDhcpActionMs;
1107                 mDhcpActionTimeoutAlarm.schedule(alarmTime);
1108             }
1109         }
1110
1111         private void stopDhcpAction() {
1112             mDhcpActionTimeoutAlarm.cancel();
1113             if (mDhcpActionInFlight) {
1114                 mCallback.onPostDhcpAction();
1115                 mDhcpActionInFlight = false;
1116             }
1117         }
1118
1119         @Override
1120         public boolean processMessage(Message msg) {
1121             switch (msg.what) {
1122                 case CMD_STOP:
1123                     transitionTo(mStoppingState);
1124                     break;
1125
1126                 case CMD_START:
1127                     Log.e(mTag, "ALERT: START received in StartedState. Please fix caller.");
1128                     break;
1129
1130                 case CMD_CONFIRM:
1131                     // TODO: Possibly introduce a second type of confirmation
1132                     // that both probes (a) on-link neighbors and (b) does
1133                     // a DHCPv4 RENEW.  We used to do this on Wi-Fi framework
1134                     // roams.
1135                     if (mIpReachabilityMonitor != null) {
1136                         mIpReachabilityMonitor.probeAll();
1137                     }
1138                     break;
1139
1140                 case EVENT_PRE_DHCP_ACTION_COMPLETE:
1141                     // It's possible to reach here if, for example, someone
1142                     // calls completedPreDhcpAction() after provisioning with
1143                     // a static IP configuration.
1144                     if (mDhcpClient != null) {
1145                         mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
1146                     }
1147                     break;
1148
1149                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
1150                     if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
1151                         transitionTo(mStoppingState);
1152                     }
1153                     break;
1154
1155                 case CMD_UPDATE_TCP_BUFFER_SIZES:
1156                     mTcpBufferSizes = (String) msg.obj;
1157                     // This cannot possibly change provisioning state.
1158                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
1159                     break;
1160
1161                 case CMD_UPDATE_HTTP_PROXY:
1162                     mHttpProxy = (ProxyInfo) msg.obj;
1163                     // This cannot possibly change provisioning state.
1164                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
1165                     break;
1166
1167                 case CMD_SET_MULTICAST_FILTER: {
1168                     mMulticastFiltering = (boolean) msg.obj;
1169                     if (mApfFilter != null) {
1170                         mApfFilter.setMulticastFilter(mMulticastFiltering);
1171                     } else {
1172                         mCallback.setFallbackMulticastFilter(mMulticastFiltering);
1173                     }
1174                     break;
1175                 }
1176
1177                 case EVENT_PROVISIONING_TIMEOUT:
1178                     handleProvisioningFailure();
1179                     break;
1180
1181                 case EVENT_DHCPACTION_TIMEOUT:
1182                     stopDhcpAction();
1183                     break;
1184
1185                 case DhcpClient.CMD_PRE_DHCP_ACTION:
1186                     if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
1187                         ensureDhcpAction();
1188                     } else {
1189                         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
1190                     }
1191                     break;
1192
1193                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
1194                     clearIPv4Address();
1195                     break;
1196
1197                 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
1198                     final LinkAddress ipAddress = (LinkAddress) msg.obj;
1199                     if (setIPv4Address(ipAddress)) {
1200                         mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
1201                     } else {
1202                         Log.e(mTag, "Failed to set IPv4 address!");
1203                         dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
1204                                 new LinkProperties(mLinkProperties));
1205                         transitionTo(mStoppingState);
1206                     }
1207                     break;
1208                 }
1209
1210                 // This message is only received when:
1211                 //
1212                 //     a) initial address acquisition succeeds,
1213                 //     b) renew succeeds or is NAK'd,
1214                 //     c) rebind succeeds or is NAK'd, or
1215                 //     c) the lease expires,
1216                 //
1217                 // but never when initial address acquisition fails. The latter
1218                 // condition is now governed by the provisioning timeout.
1219                 case DhcpClient.CMD_POST_DHCP_ACTION:
1220                     stopDhcpAction();
1221
1222                     switch (msg.arg1) {
1223                         case DhcpClient.DHCP_SUCCESS:
1224                             handleIPv4Success((DhcpResults) msg.obj);
1225                             break;
1226                         case DhcpClient.DHCP_FAILURE:
1227                             handleIPv4Failure();
1228                             break;
1229                         default:
1230                             Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
1231                     }
1232                     break;
1233
1234                 case DhcpClient.CMD_ON_QUIT:
1235                     // DHCPv4 quit early for some reason.
1236                     Log.e(mTag, "Unexpected CMD_ON_QUIT.");
1237                     mDhcpClient = null;
1238                     break;
1239
1240                 default:
1241                     return NOT_HANDLED;
1242             }
1243             return HANDLED;
1244         }
1245     }
1246 }