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.MultinetworkPolicyTracker;
     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     private static final int MAX_PACKET_RECORDS = 100;
    378 
    379     private static final boolean NO_CALLBACKS = false;
    380     private static final boolean SEND_CALLBACKS = true;
    381 
    382     // This must match the interface prefix in clatd.c.
    383     // TODO: Revert this hack once IpManager and Nat464Xlat work in concert.
    384     private static final String CLAT_PREFIX = "v4-";
    385 
    386     private final State mStoppedState = new StoppedState();
    387     private final State mStoppingState = new StoppingState();
    388     private final State mStartedState = new StartedState();
    389     private final State mRunningState = new RunningState();
    390 
    391     private final String mTag;
    392     private final Context mContext;
    393     private final String mInterfaceName;
    394     private final String mClatInterfaceName;
    395     @VisibleForTesting
    396     protected final Callback mCallback;
    397     private final INetworkManagementService mNwService;
    398     private final NetlinkTracker mNetlinkTracker;
    399     private final WakeupMessage mProvisioningTimeoutAlarm;
    400     private final WakeupMessage mDhcpActionTimeoutAlarm;
    401     private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
    402     private final LocalLog mLocalLog;
    403     private final LocalLog mConnectivityPacketLog;
    404     private final MessageHandlingLogger mMsgStateLogger;
    405     private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
    406 
    407     private NetworkInterface mNetworkInterface;
    408 
    409     /**
    410      * Non-final member variables accessed only from within our StateMachine.
    411      */
    412     private LinkProperties mLinkProperties;
    413     private ProvisioningConfiguration mConfiguration;
    414     private IpReachabilityMonitor mIpReachabilityMonitor;
    415     private DhcpClient mDhcpClient;
    416     private DhcpResults mDhcpResults;
    417     private String mTcpBufferSizes;
    418     private ProxyInfo mHttpProxy;
    419     private ApfFilter mApfFilter;
    420     private boolean mMulticastFiltering;
    421     private long mStartTimeMillis;
    422 
    423     public IpManager(Context context, String ifName, Callback callback)
    424                 throws IllegalArgumentException {
    425         this(context, ifName, callback, INetworkManagementService.Stub.asInterface(
    426                 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)));
    427     }
    428 
    429     /**
    430      * An expanded constructor, useful for dependency injection.
    431      */
    432     public IpManager(Context context, String ifName, Callback callback,
    433             INetworkManagementService nwService) throws IllegalArgumentException {
    434         super(IpManager.class.getSimpleName() + "." + ifName);
    435         mTag = getName();
    436 
    437         mContext = context;
    438         mInterfaceName = ifName;
    439         mClatInterfaceName = CLAT_PREFIX + ifName;
    440         mCallback = new LoggingCallbackWrapper(callback);
    441         mNwService = nwService;
    442 
    443         mLocalLog = new LocalLog(MAX_LOG_RECORDS);
    444         mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS);
    445         mMsgStateLogger = new MessageHandlingLogger();
    446 
    447         mNetlinkTracker = new NetlinkTracker(
    448                 mInterfaceName,
    449                 new NetlinkTracker.Callback() {
    450                     @Override
    451                     public void update() {
    452                         sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
    453                     }
    454                 }) {
    455             @Override
    456             public void interfaceAdded(String iface) {
    457                 super.interfaceAdded(iface);
    458                 if (mClatInterfaceName.equals(iface)) {
    459                     mCallback.setNeighborDiscoveryOffload(false);
    460                 } else if (!mInterfaceName.equals(iface)) {
    461                     return;
    462                 }
    463 
    464                 final String msg = "interfaceAdded(" + iface +")";
    465                 logMsg(msg);
    466             }
    467 
    468             @Override
    469             public void interfaceRemoved(String iface) {
    470                 super.interfaceRemoved(iface);
    471                 // TODO: Also observe mInterfaceName going down and take some
    472                 // kind of appropriate action.
    473                 if (mClatInterfaceName.equals(iface)) {
    474                     // TODO: consider sending a message to the IpManager main
    475                     // StateMachine thread, in case "NDO enabled" state becomes
    476                     // tied to more things that 464xlat operation.
    477                     mCallback.setNeighborDiscoveryOffload(true);
    478                 } else if (!mInterfaceName.equals(iface)) {
    479                     return;
    480                 }
    481 
    482                 final String msg = "interfaceRemoved(" + iface +")";
    483                 logMsg(msg);
    484             }
    485 
    486             private void logMsg(String msg) {
    487                 Log.d(mTag, msg);
    488                 getHandler().post(() -> { mLocalLog.log("OBSERVED " + msg); });
    489             }
    490         };
    491 
    492         mLinkProperties = new LinkProperties();
    493         mLinkProperties.setInterfaceName(mInterfaceName);
    494 
    495         mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(mContext, getHandler(),
    496                 () -> { mLocalLog.log("OBSERVED AvoidBadWifi changed"); });
    497 
    498         mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
    499                 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
    500         mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
    501                 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
    502 
    503         // Anything the StateMachine may access must have been instantiated
    504         // before this point.
    505         configureAndStartStateMachine();
    506 
    507         // Anything that may send messages to the StateMachine must only be
    508         // configured to do so after the StateMachine has started (above).
    509         startStateMachineUpdaters();
    510     }
    511 
    512     private void configureAndStartStateMachine() {
    513         addState(mStoppedState);
    514         addState(mStartedState);
    515             addState(mRunningState, mStartedState);
    516         addState(mStoppingState);
    517 
    518         setInitialState(mStoppedState);
    519 
    520         super.start();
    521     }
    522 
    523     private void startStateMachineUpdaters() {
    524         try {
    525             mNwService.registerObserver(mNetlinkTracker);
    526         } catch (RemoteException e) {
    527             logError("Couldn't register NetlinkTracker: %s", e);
    528         }
    529 
    530         mMultinetworkPolicyTracker.start();
    531     }
    532 
    533     @Override
    534     protected void onQuitting() {
    535         mCallback.onQuit();
    536     }
    537 
    538     // Shut down this IpManager instance altogether.
    539     public void shutdown() {
    540         stop();
    541         mMultinetworkPolicyTracker.shutdown();
    542         quit();
    543     }
    544 
    545     public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
    546         return new ProvisioningConfiguration.Builder();
    547     }
    548 
    549     public void startProvisioning(ProvisioningConfiguration req) {
    550         getNetworkInterface();
    551 
    552         mCallback.setNeighborDiscoveryOffload(true);
    553         sendMessage(CMD_START, new ProvisioningConfiguration(req));
    554     }
    555 
    556     // TODO: Delete this.
    557     public void startProvisioning(StaticIpConfiguration staticIpConfig) {
    558         startProvisioning(buildProvisioningConfiguration()
    559                 .withStaticConfiguration(staticIpConfig)
    560                 .build());
    561     }
    562 
    563     public void startProvisioning() {
    564         startProvisioning(new ProvisioningConfiguration());
    565     }
    566 
    567     public void stop() {
    568         sendMessage(CMD_STOP);
    569     }
    570 
    571     public void confirmConfiguration() {
    572         sendMessage(CMD_CONFIRM);
    573     }
    574 
    575     public void completedPreDhcpAction() {
    576         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
    577     }
    578 
    579     /**
    580      * Set the TCP buffer sizes to use.
    581      *
    582      * This may be called, repeatedly, at any time before or after a call to
    583      * #startProvisioning(). The setting is cleared upon calling #stop().
    584      */
    585     public void setTcpBufferSizes(String tcpBufferSizes) {
    586         sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
    587     }
    588 
    589     /**
    590      * Set the HTTP Proxy configuration to use.
    591      *
    592      * This may be called, repeatedly, at any time before or after a call to
    593      * #startProvisioning(). The setting is cleared upon calling #stop().
    594      */
    595     public void setHttpProxy(ProxyInfo proxyInfo) {
    596         sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
    597     }
    598 
    599     /**
    600      * Enable or disable the multicast filter.  Attempts to use APF to accomplish the filtering,
    601      * if not, Callback.setFallbackMulticastFilter() is called.
    602      */
    603     public void setMulticastFilter(boolean enabled) {
    604         sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
    605     }
    606 
    607     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
    608         if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
    609             // Execute confirmConfiguration() and take no further action.
    610             confirmConfiguration();
    611             return;
    612         }
    613 
    614         // Thread-unsafe access to mApfFilter but just used for debugging.
    615         final ApfFilter apfFilter = mApfFilter;
    616         final ProvisioningConfiguration provisioningConfig = mConfiguration;
    617         final ApfCapabilities apfCapabilities = (provisioningConfig != null)
    618                 ? provisioningConfig.mApfCapabilities : null;
    619 
    620         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
    621         pw.println(mTag + " APF dump:");
    622         pw.increaseIndent();
    623         if (apfFilter != null) {
    624             apfFilter.dump(pw);
    625         } else {
    626             pw.print("No active ApfFilter; ");
    627             if (provisioningConfig == null) {
    628                 pw.println("IpManager not yet started.");
    629             } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) {
    630                 pw.println("Hardware does not support APF.");
    631             } else {
    632                 pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities);
    633             }
    634         }
    635         pw.decreaseIndent();
    636 
    637         pw.println();
    638         pw.println(mTag + " current ProvisioningConfiguration:");
    639         pw.increaseIndent();
    640         pw.println(Objects.toString(provisioningConfig, "N/A"));
    641         pw.decreaseIndent();
    642 
    643         pw.println();
    644         pw.println(mTag + " StateMachine dump:");
    645         pw.increaseIndent();
    646         mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
    647         pw.decreaseIndent();
    648 
    649         pw.println();
    650         pw.println(mTag + " connectivity packet log:");
    651         pw.println();
    652         pw.println("Debug with python and scapy via:");
    653         pw.println("shell$ python");
    654         pw.println(">>> from scapy import all as scapy");
    655         pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()");
    656         pw.println();
    657 
    658         pw.increaseIndent();
    659         mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
    660         pw.decreaseIndent();
    661     }
    662 
    663 
    664     /**
    665      * Internals.
    666      */
    667 
    668     @Override
    669     protected String getWhatToString(int what) {
    670         return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
    671     }
    672 
    673     @Override
    674     protected String getLogRecString(Message msg) {
    675         final String logLine = String.format(
    676                 "%s/%d %d %d %s [%s]",
    677                 mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(),
    678                 msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
    679 
    680         final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
    681         mLocalLog.log(richerLogLine);
    682         if (VDBG) {
    683             Log.d(mTag, richerLogLine);
    684         }
    685 
    686         mMsgStateLogger.reset();
    687         return logLine;
    688     }
    689 
    690     @Override
    691     protected boolean recordLogRec(Message msg) {
    692         // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
    693         // and we already log any LinkProperties change that results in an
    694         // invocation of IpManager.Callback#onLinkPropertiesChange().
    695         final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
    696         if (!shouldLog) {
    697             mMsgStateLogger.reset();
    698         }
    699         return shouldLog;
    700     }
    701 
    702     // TODO: Migrate all Log.e(...) to logError(...).
    703     private void logError(String fmt, Object... args) {
    704         final String msg = "ERROR " + String.format(fmt, args);
    705         Log.e(mTag, msg);
    706         mLocalLog.log(msg);
    707     }
    708 
    709     private void getNetworkInterface() {
    710         try {
    711             mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
    712         } catch (SocketException | NullPointerException e) {
    713             // TODO: throw new IllegalStateException.
    714             logError("Failed to get interface object: %s", e);
    715         }
    716     }
    717 
    718     // This needs to be called with care to ensure that our LinkProperties
    719     // are in sync with the actual LinkProperties of the interface. For example,
    720     // we should only call this if we know for sure that there are no IP addresses
    721     // assigned to the interface, etc.
    722     private void resetLinkProperties() {
    723         mNetlinkTracker.clearLinkProperties();
    724         mConfiguration = null;
    725         mDhcpResults = null;
    726         mTcpBufferSizes = "";
    727         mHttpProxy = null;
    728 
    729         mLinkProperties = new LinkProperties();
    730         mLinkProperties.setInterfaceName(mInterfaceName);
    731     }
    732 
    733     private void recordMetric(final int type) {
    734         if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); }
    735         final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis;
    736         mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
    737     }
    738 
    739     // For now: use WifiStateMachine's historical notion of provisioned.
    740     private static boolean isProvisioned(LinkProperties lp) {
    741         // For historical reasons, we should connect even if all we have is
    742         // an IPv4 address and nothing else.
    743         return lp.isProvisioned() || lp.hasIPv4Address();
    744     }
    745 
    746     // TODO: Investigate folding all this into the existing static function
    747     // LinkProperties.compareProvisioning() or some other single function that
    748     // takes two LinkProperties objects and returns a ProvisioningChange
    749     // object that is a correct and complete assessment of what changed, taking
    750     // account of the asymmetries described in the comments in this function.
    751     // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
    752     private ProvisioningChange compareProvisioning(
    753             LinkProperties oldLp, LinkProperties newLp) {
    754         ProvisioningChange delta;
    755 
    756         final boolean wasProvisioned = isProvisioned(oldLp);
    757         final boolean isProvisioned = isProvisioned(newLp);
    758 
    759         if (!wasProvisioned && isProvisioned) {
    760             delta = ProvisioningChange.GAINED_PROVISIONING;
    761         } else if (wasProvisioned && isProvisioned) {
    762             delta = ProvisioningChange.STILL_PROVISIONED;
    763         } else if (!wasProvisioned && !isProvisioned) {
    764             delta = ProvisioningChange.STILL_NOT_PROVISIONED;
    765         } else {
    766             // (wasProvisioned && !isProvisioned)
    767             //
    768             // Note that this is true even if we lose a configuration element
    769             // (e.g., a default gateway) that would not be required to advance
    770             // into provisioned state. This is intended: if we have a default
    771             // router and we lose it, that's a sure sign of a problem, but if
    772             // we connect to a network with no IPv4 DNS servers, we consider
    773             // that to be a network without DNS servers and connect anyway.
    774             //
    775             // See the comment below.
    776             delta = ProvisioningChange.LOST_PROVISIONING;
    777         }
    778 
    779         final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned();
    780         final boolean lostIPv4Address = oldLp.hasIPv4Address() && !newLp.hasIPv4Address();
    781         final boolean lostIPv6Router = oldLp.hasIPv6DefaultRoute() && !newLp.hasIPv6DefaultRoute();
    782 
    783         // If bad wifi avoidance is disabled, then ignore IPv6 loss of
    784         // provisioning. Otherwise, when a hotspot that loses Internet
    785         // access sends out a 0-lifetime RA to its clients, the clients
    786         // will disconnect and then reconnect, avoiding the bad hotspot,
    787         // instead of getting stuck on the bad hotspot. http://b/31827713 .
    788         //
    789         // This is incorrect because if the hotspot then regains Internet
    790         // access with a different prefix, TCP connections on the
    791         // deprecated addresses will remain stuck.
    792         //
    793         // Note that we can still be disconnected by IpReachabilityMonitor
    794         // if the IPv6 default gateway (but not the IPv6 DNS servers; see
    795         // accompanying code in IpReachabilityMonitor) is unreachable.
    796         final boolean ignoreIPv6ProvisioningLoss = !mMultinetworkPolicyTracker.getAvoidBadWifi();
    797 
    798         // Additionally:
    799         //
    800         // Partial configurations (e.g., only an IPv4 address with no DNS
    801         // servers and no default route) are accepted as long as DHCPv4
    802         // succeeds. On such a network, isProvisioned() will always return
    803         // false, because the configuration is not complete, but we want to
    804         // connect anyway. It might be a disconnected network such as a
    805         // Chromecast or a wireless printer, for example.
    806         //
    807         // Because on such a network isProvisioned() will always return false,
    808         // delta will never be LOST_PROVISIONING. So check for loss of
    809         // provisioning here too.
    810         if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
    811             delta = ProvisioningChange.LOST_PROVISIONING;
    812         }
    813 
    814         // Additionally:
    815         //
    816         // If the previous link properties had a global IPv6 address and an
    817         // IPv6 default route then also consider the loss of that default route
    818         // to be a loss of provisioning. See b/27962810.
    819         if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
    820             delta = ProvisioningChange.LOST_PROVISIONING;
    821         }
    822 
    823         return delta;
    824     }
    825 
    826     private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
    827         switch (delta) {
    828             case GAINED_PROVISIONING:
    829                 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
    830                 recordMetric(IpManagerEvent.PROVISIONING_OK);
    831                 mCallback.onProvisioningSuccess(newLp);
    832                 break;
    833 
    834             case LOST_PROVISIONING:
    835                 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
    836                 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
    837                 mCallback.onProvisioningFailure(newLp);
    838                 break;
    839 
    840             default:
    841                 if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
    842                 mCallback.onLinkPropertiesChange(newLp);
    843                 break;
    844         }
    845     }
    846 
    847     // Updates all IpManager-related state concerned with LinkProperties.
    848     // Returns a ProvisioningChange for possibly notifying other interested
    849     // parties that are not fronted by IpManager.
    850     private ProvisioningChange setLinkProperties(LinkProperties newLp) {
    851         if (mApfFilter != null) {
    852             mApfFilter.setLinkProperties(newLp);
    853         }
    854         if (mIpReachabilityMonitor != null) {
    855             mIpReachabilityMonitor.updateLinkProperties(newLp);
    856         }
    857 
    858         ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
    859         mLinkProperties = new LinkProperties(newLp);
    860 
    861         if (delta == ProvisioningChange.GAINED_PROVISIONING) {
    862             // TODO: Add a proper ProvisionedState and cancel the alarm in
    863             // its enter() method.
    864             mProvisioningTimeoutAlarm.cancel();
    865         }
    866 
    867         return delta;
    868     }
    869 
    870     private boolean linkPropertiesUnchanged(LinkProperties newLp) {
    871         return Objects.equals(newLp, mLinkProperties);
    872     }
    873 
    874     private LinkProperties assembleLinkProperties() {
    875         // [1] Create a new LinkProperties object to populate.
    876         LinkProperties newLp = new LinkProperties();
    877         newLp.setInterfaceName(mInterfaceName);
    878 
    879         // [2] Pull in data from netlink:
    880         //         - IPv4 addresses
    881         //         - IPv6 addresses
    882         //         - IPv6 routes
    883         //         - IPv6 DNS servers
    884         //
    885         // N.B.: this is fundamentally race-prone and should be fixed by
    886         // changing NetlinkTracker from a hybrid edge/level model to an
    887         // edge-only model, or by giving IpManager its own netlink socket(s)
    888         // so as to track all required information directly.
    889         LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
    890         newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
    891         for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
    892             newLp.addRoute(route);
    893         }
    894         addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
    895 
    896         // [3] Add in data from DHCPv4, if available.
    897         //
    898         // mDhcpResults is never shared with any other owner so we don't have
    899         // to worry about concurrent modification.
    900         if (mDhcpResults != null) {
    901             for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
    902                 newLp.addRoute(route);
    903             }
    904             addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
    905             newLp.setDomains(mDhcpResults.domains);
    906 
    907             if (mDhcpResults.mtu != 0) {
    908                 newLp.setMtu(mDhcpResults.mtu);
    909             }
    910         }
    911 
    912         // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
    913         if (!TextUtils.isEmpty(mTcpBufferSizes)) {
    914             newLp.setTcpBufferSizes(mTcpBufferSizes);
    915         }
    916         if (mHttpProxy != null) {
    917             newLp.setHttpProxy(mHttpProxy);
    918         }
    919 
    920         if (VDBG) {
    921             Log.d(mTag, "newLp{" + newLp + "}");
    922         }
    923         return newLp;
    924     }
    925 
    926     private static void addAllReachableDnsServers(
    927             LinkProperties lp, Iterable<InetAddress> dnses) {
    928         // TODO: Investigate deleting this reachability check.  We should be
    929         // able to pass everything down to netd and let netd do evaluation
    930         // and RFC6724-style sorting.
    931         for (InetAddress dns : dnses) {
    932             if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
    933                 lp.addDnsServer(dns);
    934             }
    935         }
    936     }
    937 
    938     // Returns false if we have lost provisioning, true otherwise.
    939     private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
    940         final LinkProperties newLp = assembleLinkProperties();
    941         if (linkPropertiesUnchanged(newLp)) {
    942             return true;
    943         }
    944         final ProvisioningChange delta = setLinkProperties(newLp);
    945         if (sendCallbacks) {
    946             dispatchCallback(delta, newLp);
    947         }
    948         return (delta != ProvisioningChange.LOST_PROVISIONING);
    949     }
    950 
    951     private boolean setIPv4Address(LinkAddress address) {
    952         final InterfaceConfiguration ifcg = new InterfaceConfiguration();
    953         ifcg.setLinkAddress(address);
    954         try {
    955             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
    956             if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
    957         } catch (IllegalStateException | RemoteException e) {
    958             logError("IPv4 configuration failed: %s", e);
    959             return false;
    960         }
    961         return true;
    962     }
    963 
    964     private void clearIPv4Address() {
    965         try {
    966             final InterfaceConfiguration ifcg = new InterfaceConfiguration();
    967             ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
    968             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
    969         } catch (IllegalStateException | RemoteException e) {
    970             logError("Failed to clear IPv4 address on interface %s: %s", mInterfaceName, e);
    971         }
    972     }
    973 
    974     private void handleIPv4Success(DhcpResults dhcpResults) {
    975         mDhcpResults = new DhcpResults(dhcpResults);
    976         final LinkProperties newLp = assembleLinkProperties();
    977         final ProvisioningChange delta = setLinkProperties(newLp);
    978 
    979         if (VDBG) {
    980             Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
    981         }
    982         mCallback.onNewDhcpResults(dhcpResults);
    983         dispatchCallback(delta, newLp);
    984     }
    985 
    986     private void handleIPv4Failure() {
    987         // TODO: Investigate deleting this clearIPv4Address() call.
    988         //
    989         // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
    990         // that could trigger a call to this function. If we missed handling
    991         // that message in StartedState for some reason we would still clear
    992         // any addresses upon entry to StoppedState.
    993         clearIPv4Address();
    994         mDhcpResults = null;
    995         if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
    996         mCallback.onNewDhcpResults(null);
    997 
    998         handleProvisioningFailure();
    999     }
   1000 
   1001     private void handleProvisioningFailure() {
   1002         final LinkProperties newLp = assembleLinkProperties();
   1003         ProvisioningChange delta = setLinkProperties(newLp);
   1004         // If we've gotten here and we're still not provisioned treat that as
   1005         // a total loss of provisioning.
   1006         //
   1007         // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
   1008         // there was no usable IPv6 obtained before a non-zero provisioning
   1009         // timeout expired.
   1010         //
   1011         // Regardless: GAME OVER.
   1012         if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
   1013             delta = ProvisioningChange.LOST_PROVISIONING;
   1014         }
   1015 
   1016         dispatchCallback(delta, newLp);
   1017         if (delta == ProvisioningChange.LOST_PROVISIONING) {
   1018             transitionTo(mStoppingState);
   1019         }
   1020     }
   1021 
   1022     private void doImmediateProvisioningFailure(int failureType) {
   1023         if (DBG) { Log.e(mTag, "onProvisioningFailure(): " + failureType); }
   1024         recordMetric(failureType);
   1025         mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
   1026     }
   1027 
   1028     private boolean startIPv4() {
   1029         // If we have a StaticIpConfiguration attempt to apply it and
   1030         // handle the result accordingly.
   1031         if (mConfiguration.mStaticIpConfig != null) {
   1032             if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
   1033                 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
   1034             } else {
   1035                 return false;
   1036             }
   1037         } else {
   1038             // Start DHCPv4.
   1039             mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName);
   1040             mDhcpClient.registerForPreDhcpNotification();
   1041             mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
   1042         }
   1043 
   1044         return true;
   1045     }
   1046 
   1047     private boolean startIPv6() {
   1048         // Set privacy extensions.
   1049         try {
   1050             mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
   1051             mNwService.enableIpv6(mInterfaceName);
   1052         } catch (RemoteException re) {
   1053             logError("Unable to change interface settings: %s", re);
   1054             return false;
   1055         } catch (IllegalStateException ie) {
   1056             logError("Unable to change interface settings: %s", ie);
   1057             return false;
   1058         }
   1059 
   1060         return true;
   1061     }
   1062 
   1063     private boolean startIpReachabilityMonitor() {
   1064         try {
   1065             mIpReachabilityMonitor = new IpReachabilityMonitor(
   1066                     mContext,
   1067                     mInterfaceName,
   1068                     new IpReachabilityMonitor.Callback() {
   1069                         @Override
   1070                         public void notifyLost(InetAddress ip, String logMsg) {
   1071                             mCallback.onReachabilityLost(logMsg);
   1072                         }
   1073                     },
   1074                     mMultinetworkPolicyTracker);
   1075         } catch (IllegalArgumentException iae) {
   1076             // Failed to start IpReachabilityMonitor. Log it and call
   1077             // onProvisioningFailure() immediately.
   1078             //
   1079             // See http://b/31038971.
   1080             logError("IpReachabilityMonitor failure: %s", iae);
   1081             mIpReachabilityMonitor = null;
   1082         }
   1083 
   1084         return (mIpReachabilityMonitor != null);
   1085     }
   1086 
   1087     private void stopAllIP() {
   1088         // We don't need to worry about routes, just addresses, because:
   1089         //     - disableIpv6() will clear autoconf IPv6 routes as well, and
   1090         //     - we don't get IPv4 routes from netlink
   1091         // so we neither react to nor need to wait for changes in either.
   1092 
   1093         try {
   1094             mNwService.disableIpv6(mInterfaceName);
   1095         } catch (Exception e) {
   1096             logError("Failed to disable IPv6: %s", e);
   1097         }
   1098 
   1099         try {
   1100             mNwService.clearInterfaceAddresses(mInterfaceName);
   1101         } catch (Exception e) {
   1102             logError("Failed to clear addresses: %s", e);
   1103         }
   1104     }
   1105 
   1106     class StoppedState extends State {
   1107         @Override
   1108         public void enter() {
   1109             stopAllIP();
   1110 
   1111             resetLinkProperties();
   1112             if (mStartTimeMillis > 0) {
   1113                 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
   1114                 mStartTimeMillis = 0;
   1115             }
   1116         }
   1117 
   1118         @Override
   1119         public boolean processMessage(Message msg) {
   1120             switch (msg.what) {
   1121                 case CMD_STOP:
   1122                     break;
   1123 
   1124                 case CMD_START:
   1125                     mConfiguration = (ProvisioningConfiguration) msg.obj;
   1126                     transitionTo(mStartedState);
   1127                     break;
   1128 
   1129                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
   1130                     handleLinkPropertiesUpdate(NO_CALLBACKS);
   1131                     break;
   1132 
   1133                 case CMD_UPDATE_TCP_BUFFER_SIZES:
   1134                     mTcpBufferSizes = (String) msg.obj;
   1135                     handleLinkPropertiesUpdate(NO_CALLBACKS);
   1136                     break;
   1137 
   1138                 case CMD_UPDATE_HTTP_PROXY:
   1139                     mHttpProxy = (ProxyInfo) msg.obj;
   1140                     handleLinkPropertiesUpdate(NO_CALLBACKS);
   1141                     break;
   1142 
   1143                 case CMD_SET_MULTICAST_FILTER:
   1144                     mMulticastFiltering = (boolean) msg.obj;
   1145                     break;
   1146 
   1147                 case DhcpClient.CMD_ON_QUIT:
   1148                     // Everything is already stopped.
   1149                     Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
   1150                     break;
   1151 
   1152                 default:
   1153                     return NOT_HANDLED;
   1154             }
   1155 
   1156             mMsgStateLogger.handled(this, getCurrentState());
   1157             return HANDLED;
   1158         }
   1159     }
   1160 
   1161     class StoppingState extends State {
   1162         @Override
   1163         public void enter() {
   1164             if (mDhcpClient == null) {
   1165                 // There's no DHCPv4 for which to wait; proceed to stopped.
   1166                 transitionTo(mStoppedState);
   1167             }
   1168         }
   1169 
   1170         @Override
   1171         public boolean processMessage(Message msg) {
   1172             switch (msg.what) {
   1173                 case CMD_STOP:
   1174                     break;
   1175 
   1176                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
   1177                     clearIPv4Address();
   1178                     break;
   1179 
   1180                 case DhcpClient.CMD_ON_QUIT:
   1181                     mDhcpClient = null;
   1182                     transitionTo(mStoppedState);
   1183                     break;
   1184 
   1185                 default:
   1186                     deferMessage(msg);
   1187             }
   1188 
   1189             mMsgStateLogger.handled(this, getCurrentState());
   1190             return HANDLED;
   1191         }
   1192     }
   1193 
   1194     class StartedState extends State {
   1195         @Override
   1196         public void enter() {
   1197             mStartTimeMillis = SystemClock.elapsedRealtime();
   1198 
   1199             if (mConfiguration.mProvisioningTimeoutMs > 0) {
   1200                 final long alarmTime = SystemClock.elapsedRealtime() +
   1201                         mConfiguration.mProvisioningTimeoutMs;
   1202                 mProvisioningTimeoutAlarm.schedule(alarmTime);
   1203             }
   1204 
   1205             if (readyToProceed()) {
   1206                 transitionTo(mRunningState);
   1207             } else {
   1208                 // Clear all IPv4 and IPv6 before proceeding to RunningState.
   1209                 // Clean up any leftover state from an abnormal exit from
   1210                 // tethering or during an IpManager restart.
   1211                 stopAllIP();
   1212             }
   1213         }
   1214 
   1215         @Override
   1216         public void exit() {
   1217             mProvisioningTimeoutAlarm.cancel();
   1218         }
   1219 
   1220         @Override
   1221         public boolean processMessage(Message msg) {
   1222             switch (msg.what) {
   1223                 case CMD_STOP:
   1224                     transitionTo(mStoppingState);
   1225                     break;
   1226 
   1227                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
   1228                     handleLinkPropertiesUpdate(NO_CALLBACKS);
   1229                     if (readyToProceed()) {
   1230                         transitionTo(mRunningState);
   1231                     }
   1232                     break;
   1233 
   1234                 case EVENT_PROVISIONING_TIMEOUT:
   1235                     handleProvisioningFailure();
   1236                     break;
   1237 
   1238                 default:
   1239                     // It's safe to process messages out of order because the
   1240                     // only message that can both
   1241                     //     a) be received at this time and
   1242                     //     b) affect provisioning state
   1243                     // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
   1244                     deferMessage(msg);
   1245             }
   1246 
   1247             mMsgStateLogger.handled(this, getCurrentState());
   1248             return HANDLED;
   1249         }
   1250 
   1251         boolean readyToProceed() {
   1252             return (!mLinkProperties.hasIPv4Address() &&
   1253                     !mLinkProperties.hasGlobalIPv6Address());
   1254         }
   1255     }
   1256 
   1257     class RunningState extends State {
   1258         private ConnectivityPacketTracker mPacketTracker;
   1259         private boolean mDhcpActionInFlight;
   1260 
   1261         @Override
   1262         public void enter() {
   1263             mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
   1264                     mCallback, mMulticastFiltering);
   1265             // TODO: investigate the effects of any multicast filtering racing/interfering with the
   1266             // rest of this IP configuration startup.
   1267             if (mApfFilter == null) {
   1268                 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
   1269             }
   1270 
   1271             mPacketTracker = createPacketTracker();
   1272             if (mPacketTracker != null) mPacketTracker.start();
   1273 
   1274             if (mConfiguration.mEnableIPv6 && !startIPv6()) {
   1275                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
   1276                 transitionTo(mStoppingState);
   1277                 return;
   1278             }
   1279 
   1280             if (mConfiguration.mEnableIPv4 && !startIPv4()) {
   1281                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
   1282                 transitionTo(mStoppingState);
   1283                 return;
   1284             }
   1285 
   1286             if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
   1287                 doImmediateProvisioningFailure(
   1288                         IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
   1289                 transitionTo(mStoppingState);
   1290                 return;
   1291             }
   1292         }
   1293 
   1294         @Override
   1295         public void exit() {
   1296             stopDhcpAction();
   1297 
   1298             if (mIpReachabilityMonitor != null) {
   1299                 mIpReachabilityMonitor.stop();
   1300                 mIpReachabilityMonitor = null;
   1301             }
   1302 
   1303             if (mDhcpClient != null) {
   1304                 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
   1305                 mDhcpClient.doQuit();
   1306             }
   1307 
   1308             if (mPacketTracker != null) {
   1309                 mPacketTracker.stop();
   1310                 mPacketTracker = null;
   1311             }
   1312 
   1313             if (mApfFilter != null) {
   1314                 mApfFilter.shutdown();
   1315                 mApfFilter = null;
   1316             }
   1317 
   1318             resetLinkProperties();
   1319         }
   1320 
   1321         private ConnectivityPacketTracker createPacketTracker() {
   1322             try {
   1323                 return new ConnectivityPacketTracker(mNetworkInterface, mConnectivityPacketLog);
   1324             } catch (IllegalArgumentException e) {
   1325                 return null;
   1326             }
   1327         }
   1328 
   1329         private void ensureDhcpAction() {
   1330             if (!mDhcpActionInFlight) {
   1331                 mCallback.onPreDhcpAction();
   1332                 mDhcpActionInFlight = true;
   1333                 final long alarmTime = SystemClock.elapsedRealtime() +
   1334                         mConfiguration.mRequestedPreDhcpActionMs;
   1335                 mDhcpActionTimeoutAlarm.schedule(alarmTime);
   1336             }
   1337         }
   1338 
   1339         private void stopDhcpAction() {
   1340             mDhcpActionTimeoutAlarm.cancel();
   1341             if (mDhcpActionInFlight) {
   1342                 mCallback.onPostDhcpAction();
   1343                 mDhcpActionInFlight = false;
   1344             }
   1345         }
   1346 
   1347         @Override
   1348         public boolean processMessage(Message msg) {
   1349             switch (msg.what) {
   1350                 case CMD_STOP:
   1351                     transitionTo(mStoppingState);
   1352                     break;
   1353 
   1354                 case CMD_START:
   1355                     Log.e(mTag, "ALERT: START received in StartedState. Please fix caller.");
   1356                     break;
   1357 
   1358                 case CMD_CONFIRM:
   1359                     // TODO: Possibly introduce a second type of confirmation
   1360                     // that both probes (a) on-link neighbors and (b) does
   1361                     // a DHCPv4 RENEW.  We used to do this on Wi-Fi framework
   1362                     // roams.
   1363                     if (mIpReachabilityMonitor != null) {
   1364                         mIpReachabilityMonitor.probeAll();
   1365                     }
   1366                     break;
   1367 
   1368                 case EVENT_PRE_DHCP_ACTION_COMPLETE:
   1369                     // It's possible to reach here if, for example, someone
   1370                     // calls completedPreDhcpAction() after provisioning with
   1371                     // a static IP configuration.
   1372                     if (mDhcpClient != null) {
   1373                         mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
   1374                     }
   1375                     break;
   1376 
   1377                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
   1378                     if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
   1379                         transitionTo(mStoppingState);
   1380                     }
   1381                     break;
   1382 
   1383                 case CMD_UPDATE_TCP_BUFFER_SIZES:
   1384                     mTcpBufferSizes = (String) msg.obj;
   1385                     // This cannot possibly change provisioning state.
   1386                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
   1387                     break;
   1388 
   1389                 case CMD_UPDATE_HTTP_PROXY:
   1390                     mHttpProxy = (ProxyInfo) msg.obj;
   1391                     // This cannot possibly change provisioning state.
   1392                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
   1393                     break;
   1394 
   1395                 case CMD_SET_MULTICAST_FILTER: {
   1396                     mMulticastFiltering = (boolean) msg.obj;
   1397                     if (mApfFilter != null) {
   1398                         mApfFilter.setMulticastFilter(mMulticastFiltering);
   1399                     } else {
   1400                         mCallback.setFallbackMulticastFilter(mMulticastFiltering);
   1401                     }
   1402                     break;
   1403                 }
   1404 
   1405                 case EVENT_DHCPACTION_TIMEOUT:
   1406                     stopDhcpAction();
   1407                     break;
   1408 
   1409                 case DhcpClient.CMD_PRE_DHCP_ACTION:
   1410                     if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
   1411                         ensureDhcpAction();
   1412                     } else {
   1413                         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
   1414                     }
   1415                     break;
   1416 
   1417                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
   1418                     clearIPv4Address();
   1419                     break;
   1420 
   1421                 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
   1422                     final LinkAddress ipAddress = (LinkAddress) msg.obj;
   1423                     if (setIPv4Address(ipAddress)) {
   1424                         mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
   1425                     } else {
   1426                         logError("Failed to set IPv4 address.");
   1427                         dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
   1428                                 new LinkProperties(mLinkProperties));
   1429                         transitionTo(mStoppingState);
   1430                     }
   1431                     break;
   1432                 }
   1433 
   1434                 // This message is only received when:
   1435                 //
   1436                 //     a) initial address acquisition succeeds,
   1437                 //     b) renew succeeds or is NAK'd,
   1438                 //     c) rebind succeeds or is NAK'd, or
   1439                 //     c) the lease expires,
   1440                 //
   1441                 // but never when initial address acquisition fails. The latter
   1442                 // condition is now governed by the provisioning timeout.
   1443                 case DhcpClient.CMD_POST_DHCP_ACTION:
   1444                     stopDhcpAction();
   1445 
   1446                     switch (msg.arg1) {
   1447                         case DhcpClient.DHCP_SUCCESS:
   1448                             handleIPv4Success((DhcpResults) msg.obj);
   1449                             break;
   1450                         case DhcpClient.DHCP_FAILURE:
   1451                             handleIPv4Failure();
   1452                             break;
   1453                         default:
   1454                             Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
   1455                     }
   1456                     break;
   1457 
   1458                 case DhcpClient.CMD_ON_QUIT:
   1459                     // DHCPv4 quit early for some reason.
   1460                     Log.e(mTag, "Unexpected CMD_ON_QUIT.");
   1461                     mDhcpClient = null;
   1462                     break;
   1463 
   1464                 default:
   1465                     return NOT_HANDLED;
   1466             }
   1467 
   1468             mMsgStateLogger.handled(this, getCurrentState());
   1469             return HANDLED;
   1470         }
   1471     }
   1472 
   1473     private static class MessageHandlingLogger {
   1474         public String processedInState;
   1475         public String receivedInState;
   1476 
   1477         public void reset() {
   1478             processedInState = null;
   1479             receivedInState = null;
   1480         }
   1481 
   1482         public void handled(State processedIn, IState receivedIn) {
   1483             processedInState = processedIn.getClass().getSimpleName();
   1484             receivedInState = receivedIn.getName();
   1485         }
   1486 
   1487         public String toString() {
   1488             return String.format("rcvd_in=%s, proc_in=%s",
   1489                                  receivedInState, processedInState);
   1490         }
   1491     }
   1492 }
   1493