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