• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
onPreDhcpAction()95         public void onPreDhcpAction() {}
onPostDhcpAction()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.
onNewDhcpResults(DhcpResults dhcpResults)104         public void onNewDhcpResults(DhcpResults dhcpResults) {}
105 
onProvisioningSuccess(LinkProperties newLp)106         public void onProvisioningSuccess(LinkProperties newLp) {}
onProvisioningFailure(LinkProperties newLp)107         public void onProvisioningFailure(LinkProperties newLp) {}
108 
109         // Invoked on LinkProperties changes.
onLinkPropertiesChange(LinkProperties newLp)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.
onReachabilityLost(String logMsg)114         public void onReachabilityLost(String logMsg) {}
115 
116         // Called when the IpManager state machine terminates.
onQuit()117         public void onQuit() {}
118 
119         // Install an APF program to filter incoming packets.
installPacketFilter(byte[] filter)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.
setFallbackMulticastFilter(boolean enabled)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.
setNeighborDiscoveryOffload(boolean enable)128         public void setNeighborDiscoveryOffload(boolean enable) {}
129     }
130 
131     public static class WaitForProvisioningCallback extends Callback {
132         private LinkProperties mCallbackLinkProperties;
133 
waitForProvisioning()134         public LinkProperties waitForProvisioning() {
135             synchronized (this) {
136                 try {
137                     wait();
138                 } catch (InterruptedException e) {}
139                 return mCallbackLinkProperties;
140             }
141         }
142 
143         @Override
onProvisioningSuccess(LinkProperties newLp)144         public void onProvisioningSuccess(LinkProperties newLp) {
145             synchronized (this) {
146                 mCallbackLinkProperties = newLp;
147                 notify();
148             }
149         }
150 
151         @Override
onProvisioningFailure(LinkProperties newLp)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 
LoggingCallbackWrapper(Callback callback)181         public LoggingCallbackWrapper(Callback callback) {
182             mCallback = callback;
183         }
184 
log(String msg)185         private void log(String msg) {
186             mLocalLog.log(PREFIX + msg);
187         }
188 
189         @Override
onPreDhcpAction()190         public void onPreDhcpAction() {
191             mCallback.onPreDhcpAction();
192             log("onPreDhcpAction()");
193         }
194         @Override
onPostDhcpAction()195         public void onPostDhcpAction() {
196             mCallback.onPostDhcpAction();
197             log("onPostDhcpAction()");
198         }
199         @Override
onNewDhcpResults(DhcpResults dhcpResults)200         public void onNewDhcpResults(DhcpResults dhcpResults) {
201             mCallback.onNewDhcpResults(dhcpResults);
202             log("onNewDhcpResults({" + dhcpResults + "})");
203         }
204         @Override
onProvisioningSuccess(LinkProperties newLp)205         public void onProvisioningSuccess(LinkProperties newLp) {
206             mCallback.onProvisioningSuccess(newLp);
207             log("onProvisioningSuccess({" + newLp + "})");
208         }
209         @Override
onProvisioningFailure(LinkProperties newLp)210         public void onProvisioningFailure(LinkProperties newLp) {
211             mCallback.onProvisioningFailure(newLp);
212             log("onProvisioningFailure({" + newLp + "})");
213         }
214         @Override
onLinkPropertiesChange(LinkProperties newLp)215         public void onLinkPropertiesChange(LinkProperties newLp) {
216             mCallback.onLinkPropertiesChange(newLp);
217             log("onLinkPropertiesChange({" + newLp + "})");
218         }
219         @Override
onReachabilityLost(String logMsg)220         public void onReachabilityLost(String logMsg) {
221             mCallback.onReachabilityLost(logMsg);
222             log("onReachabilityLost(" + logMsg + ")");
223         }
224         @Override
onQuit()225         public void onQuit() {
226             mCallback.onQuit();
227             log("onQuit()");
228         }
229         @Override
installPacketFilter(byte[] filter)230         public void installPacketFilter(byte[] filter) {
231             mCallback.installPacketFilter(filter);
232             log("installPacketFilter(byte[" + filter.length + "])");
233         }
234         @Override
setFallbackMulticastFilter(boolean enabled)235         public void setFallbackMulticastFilter(boolean enabled) {
236             mCallback.setFallbackMulticastFilter(enabled);
237             log("setFallbackMulticastFilter(" + enabled + ")");
238         }
239         @Override
setNeighborDiscoveryOffload(boolean enable)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 
withoutIPv4()281             public Builder withoutIPv4() {
282                 mConfig.mEnableIPv4 = false;
283                 return this;
284             }
285 
withoutIPv6()286             public Builder withoutIPv6() {
287                 mConfig.mEnableIPv6 = false;
288                 return this;
289             }
290 
withoutIpReachabilityMonitor()291             public Builder withoutIpReachabilityMonitor() {
292                 mConfig.mUsingIpReachabilityMonitor = false;
293                 return this;
294             }
295 
withPreDhcpAction()296             public Builder withPreDhcpAction() {
297                 mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS;
298                 return this;
299             }
300 
withPreDhcpAction(int dhcpActionTimeoutMs)301             public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
302                 mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs;
303                 return this;
304             }
305 
withStaticConfiguration(StaticIpConfiguration staticConfig)306             public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
307                 mConfig.mStaticIpConfig = staticConfig;
308                 return this;
309             }
310 
withApfCapabilities(ApfCapabilities apfCapabilities)311             public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
312                 mConfig.mApfCapabilities = apfCapabilities;
313                 return this;
314             }
315 
withProvisioningTimeoutMs(int timeoutMs)316             public Builder withProvisioningTimeoutMs(int timeoutMs) {
317                 mConfig.mProvisioningTimeoutMs = timeoutMs;
318                 return this;
319             }
320 
build()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 
ProvisioningConfiguration()334         public ProvisioningConfiguration() {}
335 
ProvisioningConfiguration(ProvisioningConfiguration other)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
toString()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 
IpManager(Context context, String ifName, Callback callback)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      */
IpManager(Context context, String ifName, Callback callback, INetworkManagementService nwService)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
onQuitting()491     protected void onQuitting() {
492         mCallback.onQuit();
493     }
494 
495     // Shut down this IpManager instance altogether.
shutdown()496     public void shutdown() {
497         stop();
498         quit();
499     }
500 
buildProvisioningConfiguration()501     public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
502         return new ProvisioningConfiguration.Builder();
503     }
504 
startProvisioning(ProvisioningConfiguration req)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.
startProvisioning(StaticIpConfiguration staticIpConfig)513     public void startProvisioning(StaticIpConfiguration staticIpConfig) {
514         startProvisioning(buildProvisioningConfiguration()
515                 .withStaticConfiguration(staticIpConfig)
516                 .build());
517     }
518 
startProvisioning()519     public void startProvisioning() {
520         startProvisioning(new ProvisioningConfiguration());
521     }
522 
stop()523     public void stop() {
524         sendMessage(CMD_STOP);
525     }
526 
confirmConfiguration()527     public void confirmConfiguration() {
528         sendMessage(CMD_CONFIRM);
529     }
530 
completedPreDhcpAction()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      */
setTcpBufferSizes(String tcpBufferSizes)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      */
setHttpProxy(ProxyInfo proxyInfo)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      */
setMulticastFilter(boolean enabled)559     public void setMulticastFilter(boolean enabled) {
560         sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
561     }
562 
dump(FileDescriptor fd, PrintWriter writer, String[] args)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
getWhatToString(int what)589     protected String getWhatToString(int what) {
590         return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
591     }
592 
593     @Override
getLogRecString(Message msg)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
recordLogRec(Message msg)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 
getNetworkInterface()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.
resetLinkProperties()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 
recordMetric(final int type)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.
isProvisioned(LinkProperties lp)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.).
compareProvisioning( LinkProperties oldLp, LinkProperties newLp)660     private 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         // If bad wifi avoidance is disabled, then ignore IPv6 loss of
692         // provisioning. Otherwise, when a hotspot that loses Internet
693         // access sends out a 0-lifetime RA to its clients, the clients
694         // will disconnect and then reconnect, avoiding the bad hotspot,
695         // instead of getting stuck on the bad hotspot. http://b/31827713 .
696         //
697         // This is incorrect because if the hotspot then regains Internet
698         // access with a different prefix, TCP connections on the
699         // deprecated addresses will remain stuck.
700         //
701         // Note that we can still be disconnected by IpReachabilityMonitor
702         // if the IPv6 default gateway (but not the IPv6 DNS servers; see
703         // accompanying code in IpReachabilityMonitor) is unreachable.
704         final boolean ignoreIPv6ProvisioningLoss = !mAvoidBadWifiTracker.currentValue();
705 
706         // Additionally:
707         //
708         // Partial configurations (e.g., only an IPv4 address with no DNS
709         // servers and no default route) are accepted as long as DHCPv4
710         // succeeds. On such a network, isProvisioned() will always return
711         // false, because the configuration is not complete, but we want to
712         // connect anyway. It might be a disconnected network such as a
713         // Chromecast or a wireless printer, for example.
714         //
715         // Because on such a network isProvisioned() will always return false,
716         // delta will never be LOST_PROVISIONING. So check for loss of
717         // provisioning here too.
718         if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
719             delta = ProvisioningChange.LOST_PROVISIONING;
720         }
721 
722         // Additionally:
723         //
724         // If the previous link properties had a global IPv6 address and an
725         // IPv6 default route then also consider the loss of that default route
726         // to be a loss of provisioning. See b/27962810.
727         if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
728             delta = ProvisioningChange.LOST_PROVISIONING;
729         }
730 
731         return delta;
732     }
733 
dispatchCallback(ProvisioningChange delta, LinkProperties newLp)734     private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
735         switch (delta) {
736             case GAINED_PROVISIONING:
737                 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
738                 recordMetric(IpManagerEvent.PROVISIONING_OK);
739                 mCallback.onProvisioningSuccess(newLp);
740                 break;
741 
742             case LOST_PROVISIONING:
743                 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
744                 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
745                 mCallback.onProvisioningFailure(newLp);
746                 break;
747 
748             default:
749                 if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
750                 mCallback.onLinkPropertiesChange(newLp);
751                 break;
752         }
753     }
754 
755     // Updates all IpManager-related state concerned with LinkProperties.
756     // Returns a ProvisioningChange for possibly notifying other interested
757     // parties that are not fronted by IpManager.
setLinkProperties(LinkProperties newLp)758     private ProvisioningChange setLinkProperties(LinkProperties newLp) {
759         if (mApfFilter != null) {
760             mApfFilter.setLinkProperties(newLp);
761         }
762         if (mIpReachabilityMonitor != null) {
763             mIpReachabilityMonitor.updateLinkProperties(newLp);
764         }
765 
766         ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
767         mLinkProperties = new LinkProperties(newLp);
768 
769         if (delta == ProvisioningChange.GAINED_PROVISIONING) {
770             // TODO: Add a proper ProvisionedState and cancel the alarm in
771             // its enter() method.
772             mProvisioningTimeoutAlarm.cancel();
773         }
774 
775         return delta;
776     }
777 
linkPropertiesUnchanged(LinkProperties newLp)778     private boolean linkPropertiesUnchanged(LinkProperties newLp) {
779         return Objects.equals(newLp, mLinkProperties);
780     }
781 
assembleLinkProperties()782     private LinkProperties assembleLinkProperties() {
783         // [1] Create a new LinkProperties object to populate.
784         LinkProperties newLp = new LinkProperties();
785         newLp.setInterfaceName(mInterfaceName);
786 
787         // [2] Pull in data from netlink:
788         //         - IPv4 addresses
789         //         - IPv6 addresses
790         //         - IPv6 routes
791         //         - IPv6 DNS servers
792         LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
793         newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
794         for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
795             newLp.addRoute(route);
796         }
797         for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
798             // Only add likely reachable DNS servers.
799             // TODO: investigate deleting this.
800             if (newLp.isReachable(dns)) {
801                 newLp.addDnsServer(dns);
802             }
803         }
804 
805         // [3] Add in data from DHCPv4, if available.
806         //
807         // mDhcpResults is never shared with any other owner so we don't have
808         // to worry about concurrent modification.
809         if (mDhcpResults != null) {
810             for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
811                 newLp.addRoute(route);
812             }
813             for (InetAddress dns : mDhcpResults.dnsServers) {
814                 // Only add likely reachable DNS servers.
815                 // TODO: investigate deleting this.
816                 if (newLp.isReachable(dns)) {
817                     newLp.addDnsServer(dns);
818                 }
819             }
820             newLp.setDomains(mDhcpResults.domains);
821 
822             if (mDhcpResults.mtu != 0) {
823                 newLp.setMtu(mDhcpResults.mtu);
824             }
825         }
826 
827         // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
828         if (!TextUtils.isEmpty(mTcpBufferSizes)) {
829             newLp.setTcpBufferSizes(mTcpBufferSizes);
830         }
831         if (mHttpProxy != null) {
832             newLp.setHttpProxy(mHttpProxy);
833         }
834 
835         if (VDBG) {
836             Log.d(mTag, "newLp{" + newLp + "}");
837         }
838         return newLp;
839     }
840 
841     // Returns false if we have lost provisioning, true otherwise.
handleLinkPropertiesUpdate(boolean sendCallbacks)842     private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
843         final LinkProperties newLp = assembleLinkProperties();
844         if (linkPropertiesUnchanged(newLp)) {
845             return true;
846         }
847         final ProvisioningChange delta = setLinkProperties(newLp);
848         if (sendCallbacks) {
849             dispatchCallback(delta, newLp);
850         }
851         return (delta != ProvisioningChange.LOST_PROVISIONING);
852     }
853 
setIPv4Address(LinkAddress address)854     private boolean setIPv4Address(LinkAddress address) {
855         final InterfaceConfiguration ifcg = new InterfaceConfiguration();
856         ifcg.setLinkAddress(address);
857         try {
858             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
859             if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
860         } catch (IllegalStateException | RemoteException e) {
861             Log.e(mTag, "IPv4 configuration failed: ", e);
862             return false;
863         }
864         return true;
865     }
866 
clearIPv4Address()867     private void clearIPv4Address() {
868         try {
869             final InterfaceConfiguration ifcg = new InterfaceConfiguration();
870             ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
871             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
872         } catch (IllegalStateException | RemoteException e) {
873             Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
874         }
875     }
876 
handleIPv4Success(DhcpResults dhcpResults)877     private void handleIPv4Success(DhcpResults dhcpResults) {
878         mDhcpResults = new DhcpResults(dhcpResults);
879         final LinkProperties newLp = assembleLinkProperties();
880         final ProvisioningChange delta = setLinkProperties(newLp);
881 
882         if (VDBG) {
883             Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
884         }
885         mCallback.onNewDhcpResults(dhcpResults);
886         dispatchCallback(delta, newLp);
887     }
888 
handleIPv4Failure()889     private void handleIPv4Failure() {
890         // TODO: Investigate deleting this clearIPv4Address() call.
891         //
892         // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
893         // that could trigger a call to this function. If we missed handling
894         // that message in StartedState for some reason we would still clear
895         // any addresses upon entry to StoppedState.
896         clearIPv4Address();
897         mDhcpResults = null;
898         if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
899         mCallback.onNewDhcpResults(null);
900 
901         handleProvisioningFailure();
902     }
903 
handleProvisioningFailure()904     private void handleProvisioningFailure() {
905         final LinkProperties newLp = assembleLinkProperties();
906         ProvisioningChange delta = setLinkProperties(newLp);
907         // If we've gotten here and we're still not provisioned treat that as
908         // a total loss of provisioning.
909         //
910         // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
911         // there was no usable IPv6 obtained before a non-zero provisioning
912         // timeout expired.
913         //
914         // Regardless: GAME OVER.
915         if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
916             delta = ProvisioningChange.LOST_PROVISIONING;
917         }
918 
919         dispatchCallback(delta, newLp);
920         if (delta == ProvisioningChange.LOST_PROVISIONING) {
921             transitionTo(mStoppingState);
922         }
923     }
924 
startIPv4()925     private boolean startIPv4() {
926         // If we have a StaticIpConfiguration attempt to apply it and
927         // handle the result accordingly.
928         if (mConfiguration.mStaticIpConfig != null) {
929             if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
930                 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
931             } else {
932                 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
933                 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
934                 mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
935                 return false;
936             }
937         } else {
938             // Start DHCPv4.
939             mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName);
940             mDhcpClient.registerForPreDhcpNotification();
941             mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
942         }
943 
944         return true;
945     }
946 
startIPv6()947     private boolean startIPv6() {
948         // Set privacy extensions.
949         try {
950             mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
951             mNwService.enableIpv6(mInterfaceName);
952         } catch (RemoteException re) {
953             Log.e(mTag, "Unable to change interface settings: " + re);
954             return false;
955         } catch (IllegalStateException ie) {
956             Log.e(mTag, "Unable to change interface settings: " + ie);
957             return false;
958         }
959 
960         return true;
961     }
962 
963 
964     class StoppedState extends State {
965         @Override
enter()966         public void enter() {
967             try {
968                 mNwService.disableIpv6(mInterfaceName);
969                 mNwService.clearInterfaceAddresses(mInterfaceName);
970             } catch (Exception e) {
971                 Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
972             }
973 
974             resetLinkProperties();
975             if (mStartTimeMillis > 0) {
976                 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
977                 mStartTimeMillis = 0;
978             }
979         }
980 
981         @Override
processMessage(Message msg)982         public boolean processMessage(Message msg) {
983             switch (msg.what) {
984                 case CMD_STOP:
985                     break;
986 
987                 case CMD_START:
988                     mConfiguration = (ProvisioningConfiguration) msg.obj;
989                     transitionTo(mStartedState);
990                     break;
991 
992                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
993                     handleLinkPropertiesUpdate(NO_CALLBACKS);
994                     break;
995 
996                 case CMD_UPDATE_TCP_BUFFER_SIZES:
997                     mTcpBufferSizes = (String) msg.obj;
998                     handleLinkPropertiesUpdate(NO_CALLBACKS);
999                     break;
1000 
1001                 case CMD_UPDATE_HTTP_PROXY:
1002                     mHttpProxy = (ProxyInfo) msg.obj;
1003                     handleLinkPropertiesUpdate(NO_CALLBACKS);
1004                     break;
1005 
1006                 case CMD_SET_MULTICAST_FILTER:
1007                     mMulticastFiltering = (boolean) msg.obj;
1008                     break;
1009 
1010                 case DhcpClient.CMD_ON_QUIT:
1011                     // Everything is already stopped.
1012                     Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
1013                     break;
1014 
1015                 default:
1016                     return NOT_HANDLED;
1017             }
1018             return HANDLED;
1019         }
1020     }
1021 
1022     class StoppingState extends State {
1023         @Override
enter()1024         public void enter() {
1025             if (mDhcpClient == null) {
1026                 // There's no DHCPv4 for which to wait; proceed to stopped.
1027                 transitionTo(mStoppedState);
1028             }
1029         }
1030 
1031         @Override
processMessage(Message msg)1032         public boolean processMessage(Message msg) {
1033             switch (msg.what) {
1034                 case DhcpClient.CMD_ON_QUIT:
1035                     mDhcpClient = null;
1036                     transitionTo(mStoppedState);
1037                     break;
1038 
1039                 default:
1040                     deferMessage(msg);
1041             }
1042             return HANDLED;
1043         }
1044     }
1045 
1046     class StartedState extends State {
1047         private boolean mDhcpActionInFlight;
1048 
1049         @Override
enter()1050         public void enter() {
1051             mStartTimeMillis = SystemClock.elapsedRealtime();
1052 
1053             mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
1054                     mCallback, mMulticastFiltering);
1055             // TODO: investigate the effects of any multicast filtering racing/interfering with the
1056             // rest of this IP configuration startup.
1057             if (mApfFilter == null) {
1058                 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
1059             }
1060 
1061             if (mConfiguration.mProvisioningTimeoutMs > 0) {
1062                 final long alarmTime = SystemClock.elapsedRealtime() +
1063                         mConfiguration.mProvisioningTimeoutMs;
1064                 mProvisioningTimeoutAlarm.schedule(alarmTime);
1065             }
1066 
1067             if (mConfiguration.mEnableIPv6) {
1068                 // TODO: Consider transitionTo(mStoppingState) if this fails.
1069                 startIPv6();
1070             }
1071 
1072             if (mConfiguration.mEnableIPv4) {
1073                 if (!startIPv4()) {
1074                     transitionTo(mStoppingState);
1075                     return;
1076                 }
1077             }
1078 
1079             if (mConfiguration.mUsingIpReachabilityMonitor) {
1080                 mIpReachabilityMonitor = new IpReachabilityMonitor(
1081                         mContext,
1082                         mInterfaceName,
1083                         new IpReachabilityMonitor.Callback() {
1084                             @Override
1085                             public void notifyLost(InetAddress ip, String logMsg) {
1086                                 mCallback.onReachabilityLost(logMsg);
1087                             }
1088                         },
1089                         mAvoidBadWifiTracker);
1090             }
1091         }
1092 
1093         @Override
exit()1094         public void exit() {
1095             mProvisioningTimeoutAlarm.cancel();
1096             stopDhcpAction();
1097 
1098             if (mIpReachabilityMonitor != null) {
1099                 mIpReachabilityMonitor.stop();
1100                 mIpReachabilityMonitor = null;
1101             }
1102 
1103             if (mDhcpClient != null) {
1104                 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
1105                 mDhcpClient.doQuit();
1106             }
1107 
1108             if (mApfFilter != null) {
1109                 mApfFilter.shutdown();
1110                 mApfFilter = null;
1111             }
1112 
1113             resetLinkProperties();
1114         }
1115 
ensureDhcpAction()1116         private void ensureDhcpAction() {
1117             if (!mDhcpActionInFlight) {
1118                 mCallback.onPreDhcpAction();
1119                 mDhcpActionInFlight = true;
1120                 final long alarmTime = SystemClock.elapsedRealtime() +
1121                         mConfiguration.mRequestedPreDhcpActionMs;
1122                 mDhcpActionTimeoutAlarm.schedule(alarmTime);
1123             }
1124         }
1125 
stopDhcpAction()1126         private void stopDhcpAction() {
1127             mDhcpActionTimeoutAlarm.cancel();
1128             if (mDhcpActionInFlight) {
1129                 mCallback.onPostDhcpAction();
1130                 mDhcpActionInFlight = false;
1131             }
1132         }
1133 
1134         @Override
processMessage(Message msg)1135         public boolean processMessage(Message msg) {
1136             switch (msg.what) {
1137                 case CMD_STOP:
1138                     transitionTo(mStoppingState);
1139                     break;
1140 
1141                 case CMD_START:
1142                     Log.e(mTag, "ALERT: START received in StartedState. Please fix caller.");
1143                     break;
1144 
1145                 case CMD_CONFIRM:
1146                     // TODO: Possibly introduce a second type of confirmation
1147                     // that both probes (a) on-link neighbors and (b) does
1148                     // a DHCPv4 RENEW.  We used to do this on Wi-Fi framework
1149                     // roams.
1150                     if (mIpReachabilityMonitor != null) {
1151                         mIpReachabilityMonitor.probeAll();
1152                     }
1153                     break;
1154 
1155                 case EVENT_PRE_DHCP_ACTION_COMPLETE:
1156                     // It's possible to reach here if, for example, someone
1157                     // calls completedPreDhcpAction() after provisioning with
1158                     // a static IP configuration.
1159                     if (mDhcpClient != null) {
1160                         mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
1161                     }
1162                     break;
1163 
1164                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
1165                     if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
1166                         transitionTo(mStoppingState);
1167                     }
1168                     break;
1169 
1170                 case CMD_UPDATE_TCP_BUFFER_SIZES:
1171                     mTcpBufferSizes = (String) msg.obj;
1172                     // This cannot possibly change provisioning state.
1173                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
1174                     break;
1175 
1176                 case CMD_UPDATE_HTTP_PROXY:
1177                     mHttpProxy = (ProxyInfo) msg.obj;
1178                     // This cannot possibly change provisioning state.
1179                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
1180                     break;
1181 
1182                 case CMD_SET_MULTICAST_FILTER: {
1183                     mMulticastFiltering = (boolean) msg.obj;
1184                     if (mApfFilter != null) {
1185                         mApfFilter.setMulticastFilter(mMulticastFiltering);
1186                     } else {
1187                         mCallback.setFallbackMulticastFilter(mMulticastFiltering);
1188                     }
1189                     break;
1190                 }
1191 
1192                 case EVENT_PROVISIONING_TIMEOUT:
1193                     handleProvisioningFailure();
1194                     break;
1195 
1196                 case EVENT_DHCPACTION_TIMEOUT:
1197                     stopDhcpAction();
1198                     break;
1199 
1200                 case DhcpClient.CMD_PRE_DHCP_ACTION:
1201                     if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
1202                         ensureDhcpAction();
1203                     } else {
1204                         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
1205                     }
1206                     break;
1207 
1208                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
1209                     clearIPv4Address();
1210                     break;
1211 
1212                 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
1213                     final LinkAddress ipAddress = (LinkAddress) msg.obj;
1214                     if (setIPv4Address(ipAddress)) {
1215                         mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
1216                     } else {
1217                         Log.e(mTag, "Failed to set IPv4 address!");
1218                         dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
1219                                 new LinkProperties(mLinkProperties));
1220                         transitionTo(mStoppingState);
1221                     }
1222                     break;
1223                 }
1224 
1225                 // This message is only received when:
1226                 //
1227                 //     a) initial address acquisition succeeds,
1228                 //     b) renew succeeds or is NAK'd,
1229                 //     c) rebind succeeds or is NAK'd, or
1230                 //     c) the lease expires,
1231                 //
1232                 // but never when initial address acquisition fails. The latter
1233                 // condition is now governed by the provisioning timeout.
1234                 case DhcpClient.CMD_POST_DHCP_ACTION:
1235                     stopDhcpAction();
1236 
1237                     switch (msg.arg1) {
1238                         case DhcpClient.DHCP_SUCCESS:
1239                             handleIPv4Success((DhcpResults) msg.obj);
1240                             break;
1241                         case DhcpClient.DHCP_FAILURE:
1242                             handleIPv4Failure();
1243                             break;
1244                         default:
1245                             Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
1246                     }
1247                     break;
1248 
1249                 case DhcpClient.CMD_ON_QUIT:
1250                     // DHCPv4 quit early for some reason.
1251                     Log.e(mTag, "Unexpected CMD_ON_QUIT.");
1252                     mDhcpClient = null;
1253                     break;
1254 
1255                 default:
1256                     return NOT_HANDLED;
1257             }
1258             return HANDLED;
1259         }
1260     }
1261 }
1262