OSDN Git Service

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