Home | History | Annotate | Download | only in ip
      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 }
   1240