Home | History | Annotate | Download | only in ip
      1 /*
      2  * Copyright (C) 2017 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.HexDump;
     20 import com.android.internal.util.MessageUtils;
     21 import com.android.internal.util.WakeupMessage;
     22 
     23 import android.content.Context;
     24 import android.net.DhcpResults;
     25 import android.net.INetd;
     26 import android.net.IpPrefix;
     27 import android.net.LinkAddress;
     28 import android.net.LinkProperties.ProvisioningChange;
     29 import android.net.LinkProperties;
     30 import android.net.Network;
     31 import android.net.ProxyInfo;
     32 import android.net.RouteInfo;
     33 import android.net.StaticIpConfiguration;
     34 import android.net.apf.ApfCapabilities;
     35 import android.net.apf.ApfFilter;
     36 import android.net.dhcp.DhcpClient;
     37 import android.net.metrics.IpConnectivityLog;
     38 import android.net.metrics.IpManagerEvent;
     39 import android.net.util.InterfaceParams;
     40 import android.net.util.MultinetworkPolicyTracker;
     41 import android.net.util.NetdService;
     42 import android.net.util.NetworkConstants;
     43 import android.net.util.SharedLog;
     44 import android.os.ConditionVariable;
     45 import android.os.INetworkManagementService;
     46 import android.os.Message;
     47 import android.os.RemoteException;
     48 import android.os.ServiceManager;
     49 import android.os.SystemClock;
     50 import android.text.TextUtils;
     51 import android.util.LocalLog;
     52 import android.util.Log;
     53 import android.util.SparseArray;
     54 
     55 import com.android.internal.annotations.VisibleForTesting;
     56 import com.android.internal.R;
     57 import com.android.internal.util.ArrayUtils;
     58 import com.android.internal.util.IndentingPrintWriter;
     59 import com.android.internal.util.IState;
     60 import com.android.internal.util.Preconditions;
     61 import com.android.internal.util.State;
     62 import com.android.internal.util.StateMachine;
     63 import com.android.server.net.NetlinkTracker;
     64 
     65 import java.io.FileDescriptor;
     66 import java.io.PrintWriter;
     67 import java.net.Inet4Address;
     68 import java.net.Inet6Address;
     69 import java.net.InetAddress;
     70 import java.net.SocketException;
     71 import java.util.ArrayList;
     72 import java.util.Collection;
     73 import java.util.HashSet;
     74 import java.util.Objects;
     75 import java.util.List;
     76 import java.util.Set;
     77 import java.util.StringJoiner;
     78 import java.util.concurrent.ConcurrentHashMap;
     79 import java.util.concurrent.CountDownLatch;
     80 import java.util.function.Predicate;
     81 import java.util.stream.Collectors;
     82 
     83 
     84 /**
     85  * IpClient
     86  *
     87  * This class provides the interface to IP-layer provisioning and maintenance
     88  * functionality that can be used by transport layers like Wi-Fi, Ethernet,
     89  * et cetera.
     90  *
     91  * [ Lifetime ]
     92  * IpClient is designed to be instantiated as soon as the interface name is
     93  * known and can be as long-lived as the class containing it (i.e. declaring
     94  * it "private final" is okay).
     95  *
     96  * @hide
     97  */
     98 public class IpClient extends StateMachine {
     99     private static final boolean DBG = false;
    100 
    101     // For message logging.
    102     private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class };
    103     private static final SparseArray<String> sWhatToString =
    104             MessageUtils.findMessageNames(sMessageClasses);
    105     // Two static concurrent hashmaps of interface name to logging classes.
    106     // One holds StateMachine logs and the other connectivity packet logs.
    107     private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>();
    108     private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>();
    109 
    110     // If |args| is non-empty, assume it's a list of interface names for which
    111     // we should print IpClient logs (filter out all others).
    112     public static void dumpAllLogs(PrintWriter writer, String[] args) {
    113         for (String ifname : sSmLogs.keySet()) {
    114             if (!ArrayUtils.isEmpty(args) && !ArrayUtils.contains(args, ifname)) continue;
    115 
    116             writer.println(String.format("--- BEGIN %s ---", ifname));
    117 
    118             final SharedLog smLog = sSmLogs.get(ifname);
    119             if (smLog != null) {
    120                 writer.println("State machine log:");
    121                 smLog.dump(null, writer, null);
    122             }
    123 
    124             writer.println("");
    125 
    126             final LocalLog pktLog = sPktLogs.get(ifname);
    127             if (pktLog != null) {
    128                 writer.println("Connectivity packet log:");
    129                 pktLog.readOnlyLocalLog().dump(null, writer, null);
    130             }
    131 
    132             writer.println(String.format("--- END %s ---", ifname));
    133         }
    134     }
    135 
    136     /**
    137      * Callbacks for handling IpClient events.
    138      *
    139      * These methods are called by IpClient on its own thread. Implementations
    140      * of this class MUST NOT carry out long-running computations or hold locks
    141      * for which there might be contention with other code calling public
    142      * methods of the same IpClient instance.
    143      */
    144     public static class Callback {
    145         // In order to receive onPreDhcpAction(), call #withPreDhcpAction()
    146         // when constructing a ProvisioningConfiguration.
    147         //
    148         // Implementations of onPreDhcpAction() must call
    149         // IpClient#completedPreDhcpAction() to indicate that DHCP is clear
    150         // to proceed.
    151         public void onPreDhcpAction() {}
    152         public void onPostDhcpAction() {}
    153 
    154         // This is purely advisory and not an indication of provisioning
    155         // success or failure.  This is only here for callers that want to
    156         // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
    157         // DHCPv4 or static IPv4 configuration failure or success can be
    158         // determined by whether or not the passed-in DhcpResults object is
    159         // null or not.
    160         public void onNewDhcpResults(DhcpResults dhcpResults) {}
    161 
    162         public void onProvisioningSuccess(LinkProperties newLp) {}
    163         public void onProvisioningFailure(LinkProperties newLp) {}
    164 
    165         // Invoked on LinkProperties changes.
    166         public void onLinkPropertiesChange(LinkProperties newLp) {}
    167 
    168         // Called when the internal IpReachabilityMonitor (if enabled) has
    169         // detected the loss of a critical number of required neighbors.
    170         public void onReachabilityLost(String logMsg) {}
    171 
    172         // Called when the IpClient state machine terminates.
    173         public void onQuit() {}
    174 
    175         // Install an APF program to filter incoming packets.
    176         public void installPacketFilter(byte[] filter) {}
    177 
    178         // Asynchronously read back the APF program & data buffer from the wifi driver.
    179         // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
    180         // buffer. In response to this request, the driver returns the data buffer asynchronously
    181         // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
    182         public void startReadPacketFilter() {}
    183 
    184         // If multicast filtering cannot be accomplished with APF, this function will be called to
    185         // actuate multicast filtering using another means.
    186         public void setFallbackMulticastFilter(boolean enabled) {}
    187 
    188         // Enabled/disable Neighbor Discover offload functionality. This is
    189         // called, for example, whenever 464xlat is being started or stopped.
    190         public void setNeighborDiscoveryOffload(boolean enable) {}
    191     }
    192 
    193     public static class WaitForProvisioningCallback extends Callback {
    194         private final ConditionVariable mCV = new ConditionVariable();
    195         private LinkProperties mCallbackLinkProperties;
    196 
    197         public LinkProperties waitForProvisioning() {
    198             mCV.block();
    199             return mCallbackLinkProperties;
    200         }
    201 
    202         @Override
    203         public void onProvisioningSuccess(LinkProperties newLp) {
    204             mCallbackLinkProperties = newLp;
    205             mCV.open();
    206         }
    207 
    208         @Override
    209         public void onProvisioningFailure(LinkProperties newLp) {
    210             mCallbackLinkProperties = null;
    211             mCV.open();
    212         }
    213     }
    214 
    215     // Use a wrapper class to log in order to ensure complete and detailed
    216     // logging. This method is lighter weight than annotations/reflection
    217     // and has the following benefits:
    218     //
    219     //     - No invoked method can be forgotten.
    220     //       Any new method added to IpClient.Callback must be overridden
    221     //       here or it will never be called.
    222     //
    223     //     - No invoking call site can be forgotten.
    224     //       Centralized logging in this way means call sites don't need to
    225     //       remember to log, and therefore no call site can be forgotten.
    226     //
    227     //     - No variation in log format among call sites.
    228     //       Encourages logging of any available arguments, and all call sites
    229     //       are necessarily logged identically.
    230     //
    231     // TODO: Find an lighter weight approach.
    232     private class LoggingCallbackWrapper extends Callback {
    233         private static final String PREFIX = "INVOKE ";
    234         private Callback mCallback;
    235 
    236         public LoggingCallbackWrapper(Callback callback) {
    237             mCallback = callback;
    238         }
    239 
    240         private void log(String msg) {
    241             mLog.log(PREFIX + msg);
    242         }
    243 
    244         @Override
    245         public void onPreDhcpAction() {
    246             mCallback.onPreDhcpAction();
    247             log("onPreDhcpAction()");
    248         }
    249         @Override
    250         public void onPostDhcpAction() {
    251             mCallback.onPostDhcpAction();
    252             log("onPostDhcpAction()");
    253         }
    254         @Override
    255         public void onNewDhcpResults(DhcpResults dhcpResults) {
    256             mCallback.onNewDhcpResults(dhcpResults);
    257             log("onNewDhcpResults({" + dhcpResults + "})");
    258         }
    259         @Override
    260         public void onProvisioningSuccess(LinkProperties newLp) {
    261             mCallback.onProvisioningSuccess(newLp);
    262             log("onProvisioningSuccess({" + newLp + "})");
    263         }
    264         @Override
    265         public void onProvisioningFailure(LinkProperties newLp) {
    266             mCallback.onProvisioningFailure(newLp);
    267             log("onProvisioningFailure({" + newLp + "})");
    268         }
    269         @Override
    270         public void onLinkPropertiesChange(LinkProperties newLp) {
    271             mCallback.onLinkPropertiesChange(newLp);
    272             log("onLinkPropertiesChange({" + newLp + "})");
    273         }
    274         @Override
    275         public void onReachabilityLost(String logMsg) {
    276             mCallback.onReachabilityLost(logMsg);
    277             log("onReachabilityLost(" + logMsg + ")");
    278         }
    279         @Override
    280         public void onQuit() {
    281             mCallback.onQuit();
    282             log("onQuit()");
    283         }
    284         @Override
    285         public void installPacketFilter(byte[] filter) {
    286             mCallback.installPacketFilter(filter);
    287             log("installPacketFilter(byte[" + filter.length + "])");
    288         }
    289         @Override
    290         public void startReadPacketFilter() {
    291             mCallback.startReadPacketFilter();
    292             log("startReadPacketFilter()");
    293         }
    294         @Override
    295         public void setFallbackMulticastFilter(boolean enabled) {
    296             mCallback.setFallbackMulticastFilter(enabled);
    297             log("setFallbackMulticastFilter(" + enabled + ")");
    298         }
    299         @Override
    300         public void setNeighborDiscoveryOffload(boolean enable) {
    301             mCallback.setNeighborDiscoveryOffload(enable);
    302             log("setNeighborDiscoveryOffload(" + enable + ")");
    303         }
    304     }
    305 
    306     /**
    307      * This class encapsulates parameters to be passed to
    308      * IpClient#startProvisioning(). A defensive copy is made by IpClient
    309      * and the values specified herein are in force until IpClient#stop()
    310      * is called.
    311      *
    312      * Example use:
    313      *
    314      *     final ProvisioningConfiguration config =
    315      *             mIpClient.buildProvisioningConfiguration()
    316      *                     .withPreDhcpAction()
    317      *                     .withProvisioningTimeoutMs(36 * 1000)
    318      *                     .build();
    319      *     mIpClient.startProvisioning(config);
    320      *     ...
    321      *     mIpClient.stop();
    322      *
    323      * The specified provisioning configuration will only be active until
    324      * IpClient#stop() is called. Future calls to IpClient#startProvisioning()
    325      * must specify the configuration again.
    326      */
    327     public static class ProvisioningConfiguration {
    328         // TODO: Delete this default timeout once those callers that care are
    329         // fixed to pass in their preferred timeout.
    330         //
    331         // We pick 36 seconds so we can send DHCP requests at
    332         //
    333         //     t=0, t=2, t=6, t=14, t=30
    334         //
    335         // allowing for 10% jitter.
    336         private static final int DEFAULT_TIMEOUT_MS = 36 * 1000;
    337 
    338         public static class Builder {
    339             private ProvisioningConfiguration mConfig = new ProvisioningConfiguration();
    340 
    341             public Builder withoutIPv4() {
    342                 mConfig.mEnableIPv4 = false;
    343                 return this;
    344             }
    345 
    346             public Builder withoutIPv6() {
    347                 mConfig.mEnableIPv6 = false;
    348                 return this;
    349             }
    350 
    351             public Builder withoutMultinetworkPolicyTracker() {
    352                 mConfig.mUsingMultinetworkPolicyTracker = false;
    353                 return this;
    354             }
    355 
    356             public Builder withoutIpReachabilityMonitor() {
    357                 mConfig.mUsingIpReachabilityMonitor = false;
    358                 return this;
    359             }
    360 
    361             public Builder withPreDhcpAction() {
    362                 mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS;
    363                 return this;
    364             }
    365 
    366             public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
    367                 mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs;
    368                 return this;
    369             }
    370 
    371             public Builder withInitialConfiguration(InitialConfiguration initialConfig) {
    372                 mConfig.mInitialConfig = initialConfig;
    373                 return this;
    374             }
    375 
    376             public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
    377                 mConfig.mStaticIpConfig = staticConfig;
    378                 return this;
    379             }
    380 
    381             public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
    382                 mConfig.mApfCapabilities = apfCapabilities;
    383                 return this;
    384             }
    385 
    386             public Builder withProvisioningTimeoutMs(int timeoutMs) {
    387                 mConfig.mProvisioningTimeoutMs = timeoutMs;
    388                 return this;
    389             }
    390 
    391             public Builder withRandomMacAddress() {
    392                 mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64;
    393                 return this;
    394             }
    395 
    396             public Builder withStableMacAddress() {
    397                 mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
    398                 return this;
    399             }
    400 
    401             public Builder withNetwork(Network network) {
    402                 mConfig.mNetwork = network;
    403                 return this;
    404             }
    405 
    406             public Builder withDisplayName(String displayName) {
    407                 mConfig.mDisplayName = displayName;
    408                 return this;
    409             }
    410 
    411             public ProvisioningConfiguration build() {
    412                 return new ProvisioningConfiguration(mConfig);
    413             }
    414         }
    415 
    416         /* package */ boolean mEnableIPv4 = true;
    417         /* package */ boolean mEnableIPv6 = true;
    418         /* package */ boolean mUsingMultinetworkPolicyTracker = true;
    419         /* package */ boolean mUsingIpReachabilityMonitor = true;
    420         /* package */ int mRequestedPreDhcpActionMs;
    421         /* package */ InitialConfiguration mInitialConfig;
    422         /* package */ StaticIpConfiguration mStaticIpConfig;
    423         /* package */ ApfCapabilities mApfCapabilities;
    424         /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
    425         /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
    426         /* package */ Network mNetwork = null;
    427         /* package */ String mDisplayName = null;
    428 
    429         public ProvisioningConfiguration() {} // used by Builder
    430 
    431         public ProvisioningConfiguration(ProvisioningConfiguration other) {
    432             mEnableIPv4 = other.mEnableIPv4;
    433             mEnableIPv6 = other.mEnableIPv6;
    434             mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
    435             mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
    436             mInitialConfig = InitialConfiguration.copy(other.mInitialConfig);
    437             mStaticIpConfig = other.mStaticIpConfig;
    438             mApfCapabilities = other.mApfCapabilities;
    439             mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
    440             mIPv6AddrGenMode = other.mIPv6AddrGenMode;
    441             mNetwork = other.mNetwork;
    442             mDisplayName = other.mDisplayName;
    443         }
    444 
    445         @Override
    446         public String toString() {
    447             return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
    448                     .add("mEnableIPv4: " + mEnableIPv4)
    449                     .add("mEnableIPv6: " + mEnableIPv6)
    450                     .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
    451                     .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
    452                     .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
    453                     .add("mInitialConfig: " + mInitialConfig)
    454                     .add("mStaticIpConfig: " + mStaticIpConfig)
    455                     .add("mApfCapabilities: " + mApfCapabilities)
    456                     .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
    457                     .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
    458                     .add("mNetwork: " + mNetwork)
    459                     .add("mDisplayName: " + mDisplayName)
    460                     .toString();
    461         }
    462 
    463         public boolean isValid() {
    464             return (mInitialConfig == null) || mInitialConfig.isValid();
    465         }
    466     }
    467 
    468     public static class InitialConfiguration {
    469         public final Set<LinkAddress> ipAddresses = new HashSet<>();
    470         public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>();
    471         public final Set<InetAddress> dnsServers = new HashSet<>();
    472         public Inet4Address gateway; // WiFi legacy behavior with static ipv4 config
    473 
    474         public static InitialConfiguration copy(InitialConfiguration config) {
    475             if (config == null) {
    476                 return null;
    477             }
    478             InitialConfiguration configCopy = new InitialConfiguration();
    479             configCopy.ipAddresses.addAll(config.ipAddresses);
    480             configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes);
    481             configCopy.dnsServers.addAll(config.dnsServers);
    482             return configCopy;
    483         }
    484 
    485         @Override
    486         public String toString() {
    487             return String.format(
    488                     "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s}, v4 gateway: %s)",
    489                     join(", ", ipAddresses), join(", ", directlyConnectedRoutes),
    490                     join(", ", dnsServers), gateway);
    491         }
    492 
    493         public boolean isValid() {
    494             if (ipAddresses.isEmpty()) {
    495                 return false;
    496             }
    497 
    498             // For every IP address, there must be at least one prefix containing that address.
    499             for (LinkAddress addr : ipAddresses) {
    500                 if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) {
    501                     return false;
    502                 }
    503             }
    504             // For every dns server, there must be at least one prefix containing that address.
    505             for (InetAddress addr : dnsServers) {
    506                 if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) {
    507                     return false;
    508                 }
    509             }
    510             // All IPv6 LinkAddresses have an RFC7421-suitable prefix length
    511             // (read: compliant with RFC4291#section2.5.4).
    512             if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) {
    513                 return false;
    514             }
    515             // If directlyConnectedRoutes contains an IPv6 default route
    516             // then ipAddresses MUST contain at least one non-ULA GUA.
    517             if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute)
    518                     && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) {
    519                 return false;
    520             }
    521             // The prefix length of routes in directlyConnectedRoutes be within reasonable
    522             // bounds for IPv6: /48-/64 just as wed accept in RIOs.
    523             if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) {
    524                 return false;
    525             }
    526             // There no more than one IPv4 address
    527             if (ipAddresses.stream().filter(Inet4Address.class::isInstance).count() > 1) {
    528                 return false;
    529             }
    530 
    531             return true;
    532         }
    533 
    534         /**
    535          * @return true if the given list of addressess and routes satisfies provisioning for this
    536          * InitialConfiguration. LinkAddresses and RouteInfo objects are not compared with equality
    537          * because addresses and routes seen by Netlink will contain additional fields like flags,
    538          * interfaces, and so on. If this InitialConfiguration has no IP address specified, the
    539          * provisioning check always fails.
    540          *
    541          * If the given list of routes is null, only addresses are taken into considerations.
    542          */
    543         public boolean isProvisionedBy(List<LinkAddress> addresses, List<RouteInfo> routes) {
    544             if (ipAddresses.isEmpty()) {
    545                 return false;
    546             }
    547 
    548             for (LinkAddress addr : ipAddresses) {
    549                 if (!any(addresses, (addrSeen) -> addr.isSameAddressAs(addrSeen))) {
    550                     return false;
    551                 }
    552             }
    553 
    554             if (routes != null) {
    555                 for (IpPrefix prefix : directlyConnectedRoutes) {
    556                     if (!any(routes, (routeSeen) -> isDirectlyConnectedRoute(routeSeen, prefix))) {
    557                         return false;
    558                     }
    559                 }
    560             }
    561 
    562             return true;
    563         }
    564 
    565         private static boolean isDirectlyConnectedRoute(RouteInfo route, IpPrefix prefix) {
    566             return !route.hasGateway() && prefix.equals(route.getDestination());
    567         }
    568 
    569         private static boolean isPrefixLengthCompliant(LinkAddress addr) {
    570             return addr.isIPv4() || isCompliantIPv6PrefixLength(addr.getPrefixLength());
    571         }
    572 
    573         private static boolean isPrefixLengthCompliant(IpPrefix prefix) {
    574             return prefix.isIPv4() || isCompliantIPv6PrefixLength(prefix.getPrefixLength());
    575         }
    576 
    577         private static boolean isCompliantIPv6PrefixLength(int prefixLength) {
    578             return (NetworkConstants.RFC6177_MIN_PREFIX_LENGTH <= prefixLength)
    579                     && (prefixLength <= NetworkConstants.RFC7421_PREFIX_LENGTH);
    580         }
    581 
    582         private static boolean isIPv6DefaultRoute(IpPrefix prefix) {
    583             return prefix.getAddress().equals(Inet6Address.ANY);
    584         }
    585 
    586         private static boolean isIPv6GUA(LinkAddress addr) {
    587             return addr.isIPv6() && addr.isGlobalPreferred();
    588         }
    589     }
    590 
    591     public static final String DUMP_ARG = "ipclient";
    592     public static final String DUMP_ARG_CONFIRM = "confirm";
    593 
    594     private static final int CMD_TERMINATE_AFTER_STOP             = 1;
    595     private static final int CMD_STOP                             = 2;
    596     private static final int CMD_START                            = 3;
    597     private static final int CMD_CONFIRM                          = 4;
    598     private static final int EVENT_PRE_DHCP_ACTION_COMPLETE       = 5;
    599     // Sent by NetlinkTracker to communicate netlink events.
    600     private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6;
    601     private static final int CMD_UPDATE_TCP_BUFFER_SIZES          = 7;
    602     private static final int CMD_UPDATE_HTTP_PROXY                = 8;
    603     private static final int CMD_SET_MULTICAST_FILTER             = 9;
    604     private static final int EVENT_PROVISIONING_TIMEOUT           = 10;
    605     private static final int EVENT_DHCPACTION_TIMEOUT             = 11;
    606     private static final int EVENT_READ_PACKET_FILTER_COMPLETE    = 12;
    607 
    608     private static final int MAX_LOG_RECORDS = 500;
    609     private static final int MAX_PACKET_RECORDS = 100;
    610 
    611     private static final boolean NO_CALLBACKS = false;
    612     private static final boolean SEND_CALLBACKS = true;
    613 
    614     // This must match the interface prefix in clatd.c.
    615     // TODO: Revert this hack once IpClient and Nat464Xlat work in concert.
    616     private static final String CLAT_PREFIX = "v4-";
    617 
    618     private static final int IMMEDIATE_FAILURE_DURATION = 0;
    619 
    620     private final State mStoppedState = new StoppedState();
    621     private final State mStoppingState = new StoppingState();
    622     private final State mStartedState = new StartedState();
    623     private final State mRunningState = new RunningState();
    624 
    625     private final String mTag;
    626     private final Context mContext;
    627     private final String mInterfaceName;
    628     private final String mClatInterfaceName;
    629     @VisibleForTesting
    630     protected final Callback mCallback;
    631     private final Dependencies mDependencies;
    632     private final CountDownLatch mShutdownLatch;
    633     private final INetworkManagementService mNwService;
    634     private final NetlinkTracker mNetlinkTracker;
    635     private final WakeupMessage mProvisioningTimeoutAlarm;
    636     private final WakeupMessage mDhcpActionTimeoutAlarm;
    637     private final SharedLog mLog;
    638     private final LocalLog mConnectivityPacketLog;
    639     private final MessageHandlingLogger mMsgStateLogger;
    640     private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
    641     private final InterfaceController mInterfaceCtrl;
    642 
    643     private InterfaceParams mInterfaceParams;
    644 
    645     /**
    646      * Non-final member variables accessed only from within our StateMachine.
    647      */
    648     private LinkProperties mLinkProperties;
    649     private ProvisioningConfiguration mConfiguration;
    650     private MultinetworkPolicyTracker mMultinetworkPolicyTracker;
    651     private IpReachabilityMonitor mIpReachabilityMonitor;
    652     private DhcpClient mDhcpClient;
    653     private DhcpResults mDhcpResults;
    654     private String mTcpBufferSizes;
    655     private ProxyInfo mHttpProxy;
    656     private ApfFilter mApfFilter;
    657     private boolean mMulticastFiltering;
    658     private long mStartTimeMillis;
    659 
    660     /**
    661      * Reading the snapshot is an asynchronous operation initiated by invoking
    662      * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
    663      * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable
    664      * signals when a new snapshot is ready.
    665      */
    666     private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable();
    667 
    668     public static class Dependencies {
    669         public INetworkManagementService getNMS() {
    670             return INetworkManagementService.Stub.asInterface(
    671                     ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
    672         }
    673 
    674         public INetd getNetd() {
    675             return NetdService.getInstance();
    676         }
    677 
    678         public InterfaceParams getInterfaceParams(String ifname) {
    679             return InterfaceParams.getByName(ifname);
    680         }
    681     }
    682 
    683     public IpClient(Context context, String ifName, Callback callback) {
    684         this(context, ifName, callback, new Dependencies());
    685     }
    686 
    687     /**
    688      * An expanded constructor, useful for dependency injection.
    689      * TODO: migrate all test users to mock IpClient directly and remove this ctor.
    690      */
    691     public IpClient(Context context, String ifName, Callback callback,
    692             INetworkManagementService nwService) {
    693         this(context, ifName, callback, new Dependencies() {
    694             @Override
    695             public INetworkManagementService getNMS() { return nwService; }
    696         });
    697     }
    698 
    699     @VisibleForTesting
    700     IpClient(Context context, String ifName, Callback callback, Dependencies deps) {
    701         super(IpClient.class.getSimpleName() + "." + ifName);
    702         Preconditions.checkNotNull(ifName);
    703         Preconditions.checkNotNull(callback);
    704 
    705         mTag = getName();
    706 
    707         mContext = context;
    708         mInterfaceName = ifName;
    709         mClatInterfaceName = CLAT_PREFIX + ifName;
    710         mCallback = new LoggingCallbackWrapper(callback);
    711         mDependencies = deps;
    712         mShutdownLatch = new CountDownLatch(1);
    713         mNwService = deps.getNMS();
    714 
    715         sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
    716         mLog = sSmLogs.get(mInterfaceName);
    717         sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
    718         mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
    719         mMsgStateLogger = new MessageHandlingLogger();
    720 
    721         // TODO: Consider creating, constructing, and passing in some kind of
    722         // InterfaceController.Dependencies class.
    723         mInterfaceCtrl = new InterfaceController(mInterfaceName, mNwService, deps.getNetd(), mLog);
    724 
    725         mNetlinkTracker = new NetlinkTracker(
    726                 mInterfaceName,
    727                 new NetlinkTracker.Callback() {
    728                     @Override
    729                     public void update() {
    730                         sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
    731                     }
    732                 }) {
    733             @Override
    734             public void interfaceAdded(String iface) {
    735                 super.interfaceAdded(iface);
    736                 if (mClatInterfaceName.equals(iface)) {
    737                     mCallback.setNeighborDiscoveryOffload(false);
    738                 } else if (!mInterfaceName.equals(iface)) {
    739                     return;
    740                 }
    741 
    742                 final String msg = "interfaceAdded(" + iface +")";
    743                 logMsg(msg);
    744             }
    745 
    746             @Override
    747             public void interfaceRemoved(String iface) {
    748                 super.interfaceRemoved(iface);
    749                 // TODO: Also observe mInterfaceName going down and take some
    750                 // kind of appropriate action.
    751                 if (mClatInterfaceName.equals(iface)) {
    752                     // TODO: consider sending a message to the IpClient main
    753                     // StateMachine thread, in case "NDO enabled" state becomes
    754                     // tied to more things that 464xlat operation.
    755                     mCallback.setNeighborDiscoveryOffload(true);
    756                 } else if (!mInterfaceName.equals(iface)) {
    757                     return;
    758                 }
    759 
    760                 final String msg = "interfaceRemoved(" + iface +")";
    761                 logMsg(msg);
    762             }
    763 
    764             private void logMsg(String msg) {
    765                 Log.d(mTag, msg);
    766                 getHandler().post(() -> { mLog.log("OBSERVED " + msg); });
    767             }
    768         };
    769 
    770         mLinkProperties = new LinkProperties();
    771         mLinkProperties.setInterfaceName(mInterfaceName);
    772 
    773         mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
    774                 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
    775         mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
    776                 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
    777 
    778         // Anything the StateMachine may access must have been instantiated
    779         // before this point.
    780         configureAndStartStateMachine();
    781 
    782         // Anything that may send messages to the StateMachine must only be
    783         // configured to do so after the StateMachine has started (above).
    784         startStateMachineUpdaters();
    785     }
    786 
    787     private void configureAndStartStateMachine() {
    788         addState(mStoppedState);
    789         addState(mStartedState);
    790             addState(mRunningState, mStartedState);
    791         addState(mStoppingState);
    792 
    793         setInitialState(mStoppedState);
    794 
    795         super.start();
    796     }
    797 
    798     private void startStateMachineUpdaters() {
    799         try {
    800             mNwService.registerObserver(mNetlinkTracker);
    801         } catch (RemoteException e) {
    802             logError("Couldn't register NetlinkTracker: %s", e);
    803         }
    804     }
    805 
    806     private void stopStateMachineUpdaters() {
    807         try {
    808             mNwService.unregisterObserver(mNetlinkTracker);
    809         } catch (RemoteException e) {
    810             logError("Couldn't unregister NetlinkTracker: %s", e);
    811         }
    812     }
    813 
    814     @Override
    815     protected void onQuitting() {
    816         mCallback.onQuit();
    817         mShutdownLatch.countDown();
    818     }
    819 
    820     // Shut down this IpClient instance altogether.
    821     public void shutdown() {
    822         stop();
    823         sendMessage(CMD_TERMINATE_AFTER_STOP);
    824     }
    825 
    826     // In order to avoid deadlock, this method MUST NOT be called on the
    827     // IpClient instance's thread. This prohibition includes code executed by
    828     // when methods on the passed-in IpClient.Callback instance are called.
    829     public void awaitShutdown() {
    830         try {
    831             mShutdownLatch.await();
    832         } catch (InterruptedException e) {
    833             mLog.e("Interrupted while awaiting shutdown: " + e);
    834         }
    835     }
    836 
    837     public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
    838         return new ProvisioningConfiguration.Builder();
    839     }
    840 
    841     public void startProvisioning(ProvisioningConfiguration req) {
    842         if (!req.isValid()) {
    843             doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
    844             return;
    845         }
    846 
    847         mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
    848         if (mInterfaceParams == null) {
    849             logError("Failed to find InterfaceParams for " + mInterfaceName);
    850             doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
    851             return;
    852         }
    853 
    854         mCallback.setNeighborDiscoveryOffload(true);
    855         sendMessage(CMD_START, new ProvisioningConfiguration(req));
    856     }
    857 
    858     // TODO: Delete this.
    859     public void startProvisioning(StaticIpConfiguration staticIpConfig) {
    860         startProvisioning(buildProvisioningConfiguration()
    861                 .withStaticConfiguration(staticIpConfig)
    862                 .build());
    863     }
    864 
    865     public void startProvisioning() {
    866         startProvisioning(new ProvisioningConfiguration());
    867     }
    868 
    869     public void stop() {
    870         sendMessage(CMD_STOP);
    871     }
    872 
    873     public void confirmConfiguration() {
    874         sendMessage(CMD_CONFIRM);
    875     }
    876 
    877     public void completedPreDhcpAction() {
    878         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
    879     }
    880 
    881     public void readPacketFilterComplete(byte[] data) {
    882         sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
    883     }
    884 
    885     /**
    886      * Set the TCP buffer sizes to use.
    887      *
    888      * This may be called, repeatedly, at any time before or after a call to
    889      * #startProvisioning(). The setting is cleared upon calling #stop().
    890      */
    891     public void setTcpBufferSizes(String tcpBufferSizes) {
    892         sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
    893     }
    894 
    895     /**
    896      * Set the HTTP Proxy configuration to use.
    897      *
    898      * This may be called, repeatedly, at any time before or after a call to
    899      * #startProvisioning(). The setting is cleared upon calling #stop().
    900      */
    901     public void setHttpProxy(ProxyInfo proxyInfo) {
    902         sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
    903     }
    904 
    905     /**
    906      * Enable or disable the multicast filter.  Attempts to use APF to accomplish the filtering,
    907      * if not, Callback.setFallbackMulticastFilter() is called.
    908      */
    909     public void setMulticastFilter(boolean enabled) {
    910         sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
    911     }
    912 
    913     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
    914         if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
    915             // Execute confirmConfiguration() and take no further action.
    916             confirmConfiguration();
    917             return;
    918         }
    919 
    920         // Thread-unsafe access to mApfFilter but just used for debugging.
    921         final ApfFilter apfFilter = mApfFilter;
    922         final ProvisioningConfiguration provisioningConfig = mConfiguration;
    923         final ApfCapabilities apfCapabilities = (provisioningConfig != null)
    924                 ? provisioningConfig.mApfCapabilities : null;
    925 
    926         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
    927         pw.println(mTag + " APF dump:");
    928         pw.increaseIndent();
    929         if (apfFilter != null) {
    930             if (apfCapabilities.hasDataAccess()) {
    931                 // Request a new snapshot, then wait for it.
    932                 mApfDataSnapshotComplete.close();
    933                 mCallback.startReadPacketFilter();
    934                 if (!mApfDataSnapshotComplete.block(1000)) {
    935                     pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT");
    936                 }
    937             }
    938             apfFilter.dump(pw);
    939 
    940         } else {
    941             pw.print("No active ApfFilter; ");
    942             if (provisioningConfig == null) {
    943                 pw.println("IpClient not yet started.");
    944             } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) {
    945                 pw.println("Hardware does not support APF.");
    946             } else {
    947                 pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities);
    948             }
    949         }
    950         pw.decreaseIndent();
    951         pw.println();
    952         pw.println(mTag + " current ProvisioningConfiguration:");
    953         pw.increaseIndent();
    954         pw.println(Objects.toString(provisioningConfig, "N/A"));
    955         pw.decreaseIndent();
    956 
    957         final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
    958         if (iprm != null) {
    959             pw.println();
    960             pw.println(mTag + " current IpReachabilityMonitor state:");
    961             pw.increaseIndent();
    962             iprm.dump(pw);
    963             pw.decreaseIndent();
    964         }
    965 
    966         pw.println();
    967         pw.println(mTag + " StateMachine dump:");
    968         pw.increaseIndent();
    969         mLog.dump(fd, pw, args);
    970         pw.decreaseIndent();
    971 
    972         pw.println();
    973         pw.println(mTag + " connectivity packet log:");
    974         pw.println();
    975         pw.println("Debug with python and scapy via:");
    976         pw.println("shell$ python");
    977         pw.println(">>> from scapy import all as scapy");
    978         pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()");
    979         pw.println();
    980 
    981         pw.increaseIndent();
    982         mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
    983         pw.decreaseIndent();
    984     }
    985 
    986 
    987     /**
    988      * Internals.
    989      */
    990 
    991     @Override
    992     protected String getWhatToString(int what) {
    993         return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
    994     }
    995 
    996     @Override
    997     protected String getLogRecString(Message msg) {
    998         final String logLine = String.format(
    999                 "%s/%d %d %d %s [%s]",
   1000                 mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
   1001                 msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
   1002 
   1003         final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
   1004         mLog.log(richerLogLine);
   1005         if (DBG) {
   1006             Log.d(mTag, richerLogLine);
   1007         }
   1008 
   1009         mMsgStateLogger.reset();
   1010         return logLine;
   1011     }
   1012 
   1013     @Override
   1014     protected boolean recordLogRec(Message msg) {
   1015         // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
   1016         // and we already log any LinkProperties change that results in an
   1017         // invocation of IpClient.Callback#onLinkPropertiesChange().
   1018         final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
   1019         if (!shouldLog) {
   1020             mMsgStateLogger.reset();
   1021         }
   1022         return shouldLog;
   1023     }
   1024 
   1025     private void logError(String fmt, Object... args) {
   1026         final String msg = "ERROR " + String.format(fmt, args);
   1027         Log.e(mTag, msg);
   1028         mLog.log(msg);
   1029     }
   1030 
   1031     // This needs to be called with care to ensure that our LinkProperties
   1032     // are in sync with the actual LinkProperties of the interface. For example,
   1033     // we should only call this if we know for sure that there are no IP addresses
   1034     // assigned to the interface, etc.
   1035     private void resetLinkProperties() {
   1036         mNetlinkTracker.clearLinkProperties();
   1037         mConfiguration = null;
   1038         mDhcpResults = null;
   1039         mTcpBufferSizes = "";
   1040         mHttpProxy = null;
   1041 
   1042         mLinkProperties = new LinkProperties();
   1043         mLinkProperties.setInterfaceName(mInterfaceName);
   1044     }
   1045 
   1046     private void recordMetric(final int type) {
   1047         // We may record error metrics prior to starting.
   1048         // Map this to IMMEDIATE_FAILURE_DURATION.
   1049         final long duration = (mStartTimeMillis > 0)
   1050                 ? (SystemClock.elapsedRealtime() - mStartTimeMillis)
   1051                 : IMMEDIATE_FAILURE_DURATION;
   1052         mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
   1053     }
   1054 
   1055     // For now: use WifiStateMachine's historical notion of provisioned.
   1056     @VisibleForTesting
   1057     static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
   1058         // For historical reasons, we should connect even if all we have is
   1059         // an IPv4 address and nothing else.
   1060         if (lp.hasIPv4Address() || lp.isProvisioned()) {
   1061             return true;
   1062         }
   1063         if (config == null) {
   1064             return false;
   1065         }
   1066 
   1067         // When an InitialConfiguration is specified, ignore any difference with previous
   1068         // properties and instead check if properties observed match the desired properties.
   1069         return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
   1070     }
   1071 
   1072     // TODO: Investigate folding all this into the existing static function
   1073     // LinkProperties.compareProvisioning() or some other single function that
   1074     // takes two LinkProperties objects and returns a ProvisioningChange
   1075     // object that is a correct and complete assessment of what changed, taking
   1076     // account of the asymmetries described in the comments in this function.
   1077     // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
   1078     private ProvisioningChange compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
   1079         ProvisioningChange delta;
   1080         InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
   1081         final boolean wasProvisioned = isProvisioned(oldLp, config);
   1082         final boolean isProvisioned = isProvisioned(newLp, config);
   1083 
   1084         if (!wasProvisioned && isProvisioned) {
   1085             delta = ProvisioningChange.GAINED_PROVISIONING;
   1086         } else if (wasProvisioned && isProvisioned) {
   1087             delta = ProvisioningChange.STILL_PROVISIONED;
   1088         } else if (!wasProvisioned && !isProvisioned) {
   1089             delta = ProvisioningChange.STILL_NOT_PROVISIONED;
   1090         } else {
   1091             // (wasProvisioned && !isProvisioned)
   1092             //
   1093             // Note that this is true even if we lose a configuration element
   1094             // (e.g., a default gateway) that would not be required to advance
   1095             // into provisioned state. This is intended: if we have a default
   1096             // router and we lose it, that's a sure sign of a problem, but if
   1097             // we connect to a network with no IPv4 DNS servers, we consider
   1098             // that to be a network without DNS servers and connect anyway.
   1099             //
   1100             // See the comment below.
   1101             delta = ProvisioningChange.LOST_PROVISIONING;
   1102         }
   1103 
   1104         final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned();
   1105         final boolean lostIPv4Address = oldLp.hasIPv4Address() && !newLp.hasIPv4Address();
   1106         final boolean lostIPv6Router = oldLp.hasIPv6DefaultRoute() && !newLp.hasIPv6DefaultRoute();
   1107 
   1108         // If bad wifi avoidance is disabled, then ignore IPv6 loss of
   1109         // provisioning. Otherwise, when a hotspot that loses Internet
   1110         // access sends out a 0-lifetime RA to its clients, the clients
   1111         // will disconnect and then reconnect, avoiding the bad hotspot,
   1112         // instead of getting stuck on the bad hotspot. http://b/31827713 .
   1113         //
   1114         // This is incorrect because if the hotspot then regains Internet
   1115         // access with a different prefix, TCP connections on the
   1116         // deprecated addresses will remain stuck.
   1117         //
   1118         // Note that we can still be disconnected by IpReachabilityMonitor
   1119         // if the IPv6 default gateway (but not the IPv6 DNS servers; see
   1120         // accompanying code in IpReachabilityMonitor) is unreachable.
   1121         final boolean ignoreIPv6ProvisioningLoss = (mMultinetworkPolicyTracker != null)
   1122                 && !mMultinetworkPolicyTracker.getAvoidBadWifi();
   1123 
   1124         // Additionally:
   1125         //
   1126         // Partial configurations (e.g., only an IPv4 address with no DNS
   1127         // servers and no default route) are accepted as long as DHCPv4
   1128         // succeeds. On such a network, isProvisioned() will always return
   1129         // false, because the configuration is not complete, but we want to
   1130         // connect anyway. It might be a disconnected network such as a
   1131         // Chromecast or a wireless printer, for example.
   1132         //
   1133         // Because on such a network isProvisioned() will always return false,
   1134         // delta will never be LOST_PROVISIONING. So check for loss of
   1135         // provisioning here too.
   1136         if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
   1137             delta = ProvisioningChange.LOST_PROVISIONING;
   1138         }
   1139 
   1140         // Additionally:
   1141         //
   1142         // If the previous link properties had a global IPv6 address and an
   1143         // IPv6 default route then also consider the loss of that default route
   1144         // to be a loss of provisioning. See b/27962810.
   1145         if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
   1146             delta = ProvisioningChange.LOST_PROVISIONING;
   1147         }
   1148 
   1149         return delta;
   1150     }
   1151 
   1152     private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
   1153         switch (delta) {
   1154             case GAINED_PROVISIONING:
   1155                 if (DBG) { Log.d(mTag, "onProvisioningSuccess()"); }
   1156                 recordMetric(IpManagerEvent.PROVISIONING_OK);
   1157                 mCallback.onProvisioningSuccess(newLp);
   1158                 break;
   1159 
   1160             case LOST_PROVISIONING:
   1161                 if (DBG) { Log.d(mTag, "onProvisioningFailure()"); }
   1162                 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
   1163                 mCallback.onProvisioningFailure(newLp);
   1164                 break;
   1165 
   1166             default:
   1167                 if (DBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
   1168                 mCallback.onLinkPropertiesChange(newLp);
   1169                 break;
   1170         }
   1171     }
   1172 
   1173     // Updates all IpClient-related state concerned with LinkProperties.
   1174     // Returns a ProvisioningChange for possibly notifying other interested
   1175     // parties that are not fronted by IpClient.
   1176     private ProvisioningChange setLinkProperties(LinkProperties newLp) {
   1177         if (mApfFilter != null) {
   1178             mApfFilter.setLinkProperties(newLp);
   1179         }
   1180         if (mIpReachabilityMonitor != null) {
   1181             mIpReachabilityMonitor.updateLinkProperties(newLp);
   1182         }
   1183 
   1184         ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
   1185         mLinkProperties = new LinkProperties(newLp);
   1186 
   1187         if (delta == ProvisioningChange.GAINED_PROVISIONING) {
   1188             // TODO: Add a proper ProvisionedState and cancel the alarm in
   1189             // its enter() method.
   1190             mProvisioningTimeoutAlarm.cancel();
   1191         }
   1192 
   1193         return delta;
   1194     }
   1195 
   1196     private LinkProperties assembleLinkProperties() {
   1197         // [1] Create a new LinkProperties object to populate.
   1198         LinkProperties newLp = new LinkProperties();
   1199         newLp.setInterfaceName(mInterfaceName);
   1200 
   1201         // [2] Pull in data from netlink:
   1202         //         - IPv4 addresses
   1203         //         - IPv6 addresses
   1204         //         - IPv6 routes
   1205         //         - IPv6 DNS servers
   1206         //
   1207         // N.B.: this is fundamentally race-prone and should be fixed by
   1208         // changing NetlinkTracker from a hybrid edge/level model to an
   1209         // edge-only model, or by giving IpClient its own netlink socket(s)
   1210         // so as to track all required information directly.
   1211         LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
   1212         newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
   1213         for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
   1214             newLp.addRoute(route);
   1215         }
   1216         addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
   1217 
   1218         // [3] Add in data from DHCPv4, if available.
   1219         //
   1220         // mDhcpResults is never shared with any other owner so we don't have
   1221         // to worry about concurrent modification.
   1222         if (mDhcpResults != null) {
   1223             for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
   1224                 newLp.addRoute(route);
   1225             }
   1226             addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
   1227             newLp.setDomains(mDhcpResults.domains);
   1228 
   1229             if (mDhcpResults.mtu != 0) {
   1230                 newLp.setMtu(mDhcpResults.mtu);
   1231             }
   1232         }
   1233 
   1234         // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
   1235         if (!TextUtils.isEmpty(mTcpBufferSizes)) {
   1236             newLp.setTcpBufferSizes(mTcpBufferSizes);
   1237         }
   1238         if (mHttpProxy != null) {
   1239             newLp.setHttpProxy(mHttpProxy);
   1240         }
   1241 
   1242         // [5] Add data from InitialConfiguration
   1243         if (mConfiguration != null && mConfiguration.mInitialConfig != null) {
   1244             InitialConfiguration config = mConfiguration.mInitialConfig;
   1245             // Add InitialConfiguration routes and dns server addresses once all addresses
   1246             // specified in the InitialConfiguration have been observed with Netlink.
   1247             if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) {
   1248                 for (IpPrefix prefix : config.directlyConnectedRoutes) {
   1249                     newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName));
   1250                 }
   1251             }
   1252             addAllReachableDnsServers(newLp, config.dnsServers);
   1253         }
   1254         final LinkProperties oldLp = mLinkProperties;
   1255         if (DBG) {
   1256             Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
   1257                     netlinkLinkProperties, newLp, oldLp));
   1258         }
   1259 
   1260         // TODO: also learn via netlink routes specified by an InitialConfiguration and specified
   1261         // from a static IP v4 config instead of manually patching them in in steps [3] and [5].
   1262         return newLp;
   1263     }
   1264 
   1265     private static void addAllReachableDnsServers(
   1266             LinkProperties lp, Iterable<InetAddress> dnses) {
   1267         // TODO: Investigate deleting this reachability check.  We should be
   1268         // able to pass everything down to netd and let netd do evaluation
   1269         // and RFC6724-style sorting.
   1270         for (InetAddress dns : dnses) {
   1271             if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
   1272                 lp.addDnsServer(dns);
   1273             }
   1274         }
   1275     }
   1276 
   1277     // Returns false if we have lost provisioning, true otherwise.
   1278     private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
   1279         final LinkProperties newLp = assembleLinkProperties();
   1280         if (Objects.equals(newLp, mLinkProperties)) {
   1281             return true;
   1282         }
   1283         final ProvisioningChange delta = setLinkProperties(newLp);
   1284         if (sendCallbacks) {
   1285             dispatchCallback(delta, newLp);
   1286         }
   1287         return (delta != ProvisioningChange.LOST_PROVISIONING);
   1288     }
   1289 
   1290     private void handleIPv4Success(DhcpResults dhcpResults) {
   1291         mDhcpResults = new DhcpResults(dhcpResults);
   1292         final LinkProperties newLp = assembleLinkProperties();
   1293         final ProvisioningChange delta = setLinkProperties(newLp);
   1294 
   1295         if (DBG) {
   1296             Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
   1297         }
   1298         mCallback.onNewDhcpResults(dhcpResults);
   1299         dispatchCallback(delta, newLp);
   1300     }
   1301 
   1302     private void handleIPv4Failure() {
   1303         // TODO: Investigate deleting this clearIPv4Address() call.
   1304         //
   1305         // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
   1306         // that could trigger a call to this function. If we missed handling
   1307         // that message in StartedState for some reason we would still clear
   1308         // any addresses upon entry to StoppedState.
   1309         mInterfaceCtrl.clearIPv4Address();
   1310         mDhcpResults = null;
   1311         if (DBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
   1312         mCallback.onNewDhcpResults(null);
   1313 
   1314         handleProvisioningFailure();
   1315     }
   1316 
   1317     private void handleProvisioningFailure() {
   1318         final LinkProperties newLp = assembleLinkProperties();
   1319         ProvisioningChange delta = setLinkProperties(newLp);
   1320         // If we've gotten here and we're still not provisioned treat that as
   1321         // a total loss of provisioning.
   1322         //
   1323         // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
   1324         // there was no usable IPv6 obtained before a non-zero provisioning
   1325         // timeout expired.
   1326         //
   1327         // Regardless: GAME OVER.
   1328         if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
   1329             delta = ProvisioningChange.LOST_PROVISIONING;
   1330         }
   1331 
   1332         dispatchCallback(delta, newLp);
   1333         if (delta == ProvisioningChange.LOST_PROVISIONING) {
   1334             transitionTo(mStoppingState);
   1335         }
   1336     }
   1337 
   1338     private void doImmediateProvisioningFailure(int failureType) {
   1339         logError("onProvisioningFailure(): %s", failureType);
   1340         recordMetric(failureType);
   1341         mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
   1342     }
   1343 
   1344     private boolean startIPv4() {
   1345         // If we have a StaticIpConfiguration attempt to apply it and
   1346         // handle the result accordingly.
   1347         if (mConfiguration.mStaticIpConfig != null) {
   1348             if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
   1349                 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
   1350             } else {
   1351                 return false;
   1352             }
   1353         } else {
   1354             // Start DHCPv4.
   1355             mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
   1356             mDhcpClient.registerForPreDhcpNotification();
   1357             mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
   1358         }
   1359 
   1360         return true;
   1361     }
   1362 
   1363     private boolean startIPv6() {
   1364         return mInterfaceCtrl.setIPv6PrivacyExtensions(true) &&
   1365                mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode) &&
   1366                mInterfaceCtrl.enableIPv6();
   1367     }
   1368 
   1369     private boolean applyInitialConfig(InitialConfiguration config) {
   1370         // TODO: also support specifying a static IPv4 configuration in InitialConfiguration.
   1371         for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIPv6)) {
   1372             if (!mInterfaceCtrl.addAddress(addr)) return false;
   1373         }
   1374 
   1375         return true;
   1376     }
   1377 
   1378     private boolean startIpReachabilityMonitor() {
   1379         try {
   1380             mIpReachabilityMonitor = new IpReachabilityMonitor(
   1381                     mContext,
   1382                     mInterfaceParams,
   1383                     getHandler(),
   1384                     mLog,
   1385                     new IpReachabilityMonitor.Callback() {
   1386                         @Override
   1387                         public void notifyLost(InetAddress ip, String logMsg) {
   1388                             mCallback.onReachabilityLost(logMsg);
   1389                         }
   1390                     },
   1391                     mMultinetworkPolicyTracker);
   1392         } catch (IllegalArgumentException iae) {
   1393             // Failed to start IpReachabilityMonitor. Log it and call
   1394             // onProvisioningFailure() immediately.
   1395             //
   1396             // See http://b/31038971.
   1397             logError("IpReachabilityMonitor failure: %s", iae);
   1398             mIpReachabilityMonitor = null;
   1399         }
   1400 
   1401         return (mIpReachabilityMonitor != null);
   1402     }
   1403 
   1404     private void stopAllIP() {
   1405         // We don't need to worry about routes, just addresses, because:
   1406         //     - disableIpv6() will clear autoconf IPv6 routes as well, and
   1407         //     - we don't get IPv4 routes from netlink
   1408         // so we neither react to nor need to wait for changes in either.
   1409 
   1410         mInterfaceCtrl.disableIPv6();
   1411         mInterfaceCtrl.clearAllAddresses();
   1412     }
   1413 
   1414     class StoppedState extends State {
   1415         @Override
   1416         public void enter() {
   1417             stopAllIP();
   1418 
   1419             resetLinkProperties();
   1420             if (mStartTimeMillis > 0) {
   1421                 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
   1422                 mStartTimeMillis = 0;
   1423             }
   1424         }
   1425 
   1426         @Override
   1427         public boolean processMessage(Message msg) {
   1428             switch (msg.what) {
   1429                 case CMD_TERMINATE_AFTER_STOP:
   1430                     stopStateMachineUpdaters();
   1431                     quit();
   1432                     break;
   1433 
   1434                 case CMD_STOP:
   1435                     break;
   1436 
   1437                 case CMD_START:
   1438                     mConfiguration = (ProvisioningConfiguration) msg.obj;
   1439                     transitionTo(mStartedState);
   1440                     break;
   1441 
   1442                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
   1443                     handleLinkPropertiesUpdate(NO_CALLBACKS);
   1444                     break;
   1445 
   1446                 case CMD_UPDATE_TCP_BUFFER_SIZES:
   1447                     mTcpBufferSizes = (String) msg.obj;
   1448                     handleLinkPropertiesUpdate(NO_CALLBACKS);
   1449                     break;
   1450 
   1451                 case CMD_UPDATE_HTTP_PROXY:
   1452                     mHttpProxy = (ProxyInfo) msg.obj;
   1453                     handleLinkPropertiesUpdate(NO_CALLBACKS);
   1454                     break;
   1455 
   1456                 case CMD_SET_MULTICAST_FILTER:
   1457                     mMulticastFiltering = (boolean) msg.obj;
   1458                     break;
   1459 
   1460                 case DhcpClient.CMD_ON_QUIT:
   1461                     // Everything is already stopped.
   1462                     logError("Unexpected CMD_ON_QUIT (already stopped).");
   1463                     break;
   1464 
   1465                 default:
   1466                     return NOT_HANDLED;
   1467             }
   1468 
   1469             mMsgStateLogger.handled(this, getCurrentState());
   1470             return HANDLED;
   1471         }
   1472     }
   1473 
   1474     class StoppingState extends State {
   1475         @Override
   1476         public void enter() {
   1477             if (mDhcpClient == null) {
   1478                 // There's no DHCPv4 for which to wait; proceed to stopped.
   1479                 transitionTo(mStoppedState);
   1480             }
   1481         }
   1482 
   1483         @Override
   1484         public boolean processMessage(Message msg) {
   1485             switch (msg.what) {
   1486                 case CMD_STOP:
   1487                     break;
   1488 
   1489                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
   1490                     mInterfaceCtrl.clearIPv4Address();
   1491                     break;
   1492 
   1493                 case DhcpClient.CMD_ON_QUIT:
   1494                     mDhcpClient = null;
   1495                     transitionTo(mStoppedState);
   1496                     break;
   1497 
   1498                 default:
   1499                     deferMessage(msg);
   1500             }
   1501 
   1502             mMsgStateLogger.handled(this, getCurrentState());
   1503             return HANDLED;
   1504         }
   1505     }
   1506 
   1507     class StartedState extends State {
   1508         @Override
   1509         public void enter() {
   1510             mStartTimeMillis = SystemClock.elapsedRealtime();
   1511 
   1512             if (mConfiguration.mProvisioningTimeoutMs > 0) {
   1513                 final long alarmTime = SystemClock.elapsedRealtime() +
   1514                         mConfiguration.mProvisioningTimeoutMs;
   1515                 mProvisioningTimeoutAlarm.schedule(alarmTime);
   1516             }
   1517 
   1518             if (readyToProceed()) {
   1519                 transitionTo(mRunningState);
   1520             } else {
   1521                 // Clear all IPv4 and IPv6 before proceeding to RunningState.
   1522                 // Clean up any leftover state from an abnormal exit from
   1523                 // tethering or during an IpClient restart.
   1524                 stopAllIP();
   1525             }
   1526         }
   1527 
   1528         @Override
   1529         public void exit() {
   1530             mProvisioningTimeoutAlarm.cancel();
   1531         }
   1532 
   1533         @Override
   1534         public boolean processMessage(Message msg) {
   1535             switch (msg.what) {
   1536                 case CMD_STOP:
   1537                     transitionTo(mStoppingState);
   1538                     break;
   1539 
   1540                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
   1541                     handleLinkPropertiesUpdate(NO_CALLBACKS);
   1542                     if (readyToProceed()) {
   1543                         transitionTo(mRunningState);
   1544                     }
   1545                     break;
   1546 
   1547                 case EVENT_PROVISIONING_TIMEOUT:
   1548                     handleProvisioningFailure();
   1549                     break;
   1550 
   1551                 default:
   1552                     // It's safe to process messages out of order because the
   1553                     // only message that can both
   1554                     //     a) be received at this time and
   1555                     //     b) affect provisioning state
   1556                     // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
   1557                     deferMessage(msg);
   1558             }
   1559 
   1560             mMsgStateLogger.handled(this, getCurrentState());
   1561             return HANDLED;
   1562         }
   1563 
   1564         boolean readyToProceed() {
   1565             return (!mLinkProperties.hasIPv4Address() &&
   1566                     !mLinkProperties.hasGlobalIPv6Address());
   1567         }
   1568     }
   1569 
   1570     class RunningState extends State {
   1571         private ConnectivityPacketTracker mPacketTracker;
   1572         private boolean mDhcpActionInFlight;
   1573 
   1574         @Override
   1575         public void enter() {
   1576             ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
   1577             apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
   1578             apfConfig.multicastFilter = mMulticastFiltering;
   1579             // Get the Configuration for ApfFilter from Context
   1580             apfConfig.ieee802_3Filter =
   1581                     mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
   1582             apfConfig.ethTypeBlackList =
   1583                     mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
   1584             mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
   1585             // TODO: investigate the effects of any multicast filtering racing/interfering with the
   1586             // rest of this IP configuration startup.
   1587             if (mApfFilter == null) {
   1588                 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
   1589             }
   1590 
   1591             mPacketTracker = createPacketTracker();
   1592             if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
   1593 
   1594             if (mConfiguration.mEnableIPv6 && !startIPv6()) {
   1595                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
   1596                 transitionTo(mStoppingState);
   1597                 return;
   1598             }
   1599 
   1600             if (mConfiguration.mEnableIPv4 && !startIPv4()) {
   1601                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
   1602                 transitionTo(mStoppingState);
   1603                 return;
   1604             }
   1605 
   1606             final InitialConfiguration config = mConfiguration.mInitialConfig;
   1607             if ((config != null) && !applyInitialConfig(config)) {
   1608                 // TODO introduce a new IpManagerEvent constant to distinguish this error case.
   1609                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
   1610                 transitionTo(mStoppingState);
   1611                 return;
   1612             }
   1613 
   1614             if (mConfiguration.mUsingMultinetworkPolicyTracker) {
   1615                 mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(
   1616                         mContext, getHandler(),
   1617                         () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
   1618                 mMultinetworkPolicyTracker.start();
   1619             }
   1620 
   1621             if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
   1622                 doImmediateProvisioningFailure(
   1623                         IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
   1624                 transitionTo(mStoppingState);
   1625                 return;
   1626             }
   1627         }
   1628 
   1629         @Override
   1630         public void exit() {
   1631             stopDhcpAction();
   1632 
   1633             if (mIpReachabilityMonitor != null) {
   1634                 mIpReachabilityMonitor.stop();
   1635                 mIpReachabilityMonitor = null;
   1636             }
   1637 
   1638             if (mMultinetworkPolicyTracker != null) {
   1639                 mMultinetworkPolicyTracker.shutdown();
   1640                 mMultinetworkPolicyTracker = null;
   1641             }
   1642 
   1643             if (mDhcpClient != null) {
   1644                 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
   1645                 mDhcpClient.doQuit();
   1646             }
   1647 
   1648             if (mPacketTracker != null) {
   1649                 mPacketTracker.stop();
   1650                 mPacketTracker = null;
   1651             }
   1652 
   1653             if (mApfFilter != null) {
   1654                 mApfFilter.shutdown();
   1655                 mApfFilter = null;
   1656             }
   1657 
   1658             resetLinkProperties();
   1659         }
   1660 
   1661         private ConnectivityPacketTracker createPacketTracker() {
   1662             try {
   1663                 return new ConnectivityPacketTracker(
   1664                         getHandler(), mInterfaceParams, mConnectivityPacketLog);
   1665             } catch (IllegalArgumentException e) {
   1666                 return null;
   1667             }
   1668         }
   1669 
   1670         private void ensureDhcpAction() {
   1671             if (!mDhcpActionInFlight) {
   1672                 mCallback.onPreDhcpAction();
   1673                 mDhcpActionInFlight = true;
   1674                 final long alarmTime = SystemClock.elapsedRealtime() +
   1675                         mConfiguration.mRequestedPreDhcpActionMs;
   1676                 mDhcpActionTimeoutAlarm.schedule(alarmTime);
   1677             }
   1678         }
   1679 
   1680         private void stopDhcpAction() {
   1681             mDhcpActionTimeoutAlarm.cancel();
   1682             if (mDhcpActionInFlight) {
   1683                 mCallback.onPostDhcpAction();
   1684                 mDhcpActionInFlight = false;
   1685             }
   1686         }
   1687 
   1688         @Override
   1689         public boolean processMessage(Message msg) {
   1690             switch (msg.what) {
   1691                 case CMD_STOP:
   1692                     transitionTo(mStoppingState);
   1693                     break;
   1694 
   1695                 case CMD_START:
   1696                     logError("ALERT: START received in StartedState. Please fix caller.");
   1697                     break;
   1698 
   1699                 case CMD_CONFIRM:
   1700                     // TODO: Possibly introduce a second type of confirmation
   1701                     // that both probes (a) on-link neighbors and (b) does
   1702                     // a DHCPv4 RENEW.  We used to do this on Wi-Fi framework
   1703                     // roams.
   1704                     if (mIpReachabilityMonitor != null) {
   1705                         mIpReachabilityMonitor.probeAll();
   1706                     }
   1707                     break;
   1708 
   1709                 case EVENT_PRE_DHCP_ACTION_COMPLETE:
   1710                     // It's possible to reach here if, for example, someone
   1711                     // calls completedPreDhcpAction() after provisioning with
   1712                     // a static IP configuration.
   1713                     if (mDhcpClient != null) {
   1714                         mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
   1715                     }
   1716                     break;
   1717 
   1718                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
   1719                     if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
   1720                         transitionTo(mStoppingState);
   1721                     }
   1722                     break;
   1723 
   1724                 case CMD_UPDATE_TCP_BUFFER_SIZES:
   1725                     mTcpBufferSizes = (String) msg.obj;
   1726                     // This cannot possibly change provisioning state.
   1727                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
   1728                     break;
   1729 
   1730                 case CMD_UPDATE_HTTP_PROXY:
   1731                     mHttpProxy = (ProxyInfo) msg.obj;
   1732                     // This cannot possibly change provisioning state.
   1733                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
   1734                     break;
   1735 
   1736                 case CMD_SET_MULTICAST_FILTER: {
   1737                     mMulticastFiltering = (boolean) msg.obj;
   1738                     if (mApfFilter != null) {
   1739                         mApfFilter.setMulticastFilter(mMulticastFiltering);
   1740                     } else {
   1741                         mCallback.setFallbackMulticastFilter(mMulticastFiltering);
   1742                     }
   1743                     break;
   1744                 }
   1745 
   1746                 case EVENT_READ_PACKET_FILTER_COMPLETE: {
   1747                     if (mApfFilter != null) {
   1748                         mApfFilter.setDataSnapshot((byte[]) msg.obj);
   1749                     }
   1750                     mApfDataSnapshotComplete.open();
   1751                     break;
   1752                 }
   1753 
   1754                 case EVENT_DHCPACTION_TIMEOUT:
   1755                     stopDhcpAction();
   1756                     break;
   1757 
   1758                 case DhcpClient.CMD_PRE_DHCP_ACTION:
   1759                     if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
   1760                         ensureDhcpAction();
   1761                     } else {
   1762                         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
   1763                     }
   1764                     break;
   1765 
   1766                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
   1767                     mInterfaceCtrl.clearIPv4Address();
   1768                     break;
   1769 
   1770                 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
   1771                     final LinkAddress ipAddress = (LinkAddress) msg.obj;
   1772                     if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
   1773                         mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
   1774                     } else {
   1775                         logError("Failed to set IPv4 address.");
   1776                         dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
   1777                                 new LinkProperties(mLinkProperties));
   1778                         transitionTo(mStoppingState);
   1779                     }
   1780                     break;
   1781                 }
   1782 
   1783                 // This message is only received when:
   1784                 //
   1785                 //     a) initial address acquisition succeeds,
   1786                 //     b) renew succeeds or is NAK'd,
   1787                 //     c) rebind succeeds or is NAK'd, or
   1788                 //     c) the lease expires,
   1789                 //
   1790                 // but never when initial address acquisition fails. The latter
   1791                 // condition is now governed by the provisioning timeout.
   1792                 case DhcpClient.CMD_POST_DHCP_ACTION:
   1793                     stopDhcpAction();
   1794 
   1795                     switch (msg.arg1) {
   1796                         case DhcpClient.DHCP_SUCCESS:
   1797                             handleIPv4Success((DhcpResults) msg.obj);
   1798                             break;
   1799                         case DhcpClient.DHCP_FAILURE:
   1800                             handleIPv4Failure();
   1801                             break;
   1802                         default:
   1803                             logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
   1804                     }
   1805                     break;
   1806 
   1807                 case DhcpClient.CMD_ON_QUIT:
   1808                     // DHCPv4 quit early for some reason.
   1809                     logError("Unexpected CMD_ON_QUIT.");
   1810                     mDhcpClient = null;
   1811                     break;
   1812 
   1813                 default:
   1814                     return NOT_HANDLED;
   1815             }
   1816 
   1817             mMsgStateLogger.handled(this, getCurrentState());
   1818             return HANDLED;
   1819         }
   1820     }
   1821 
   1822     private static class MessageHandlingLogger {
   1823         public String processedInState;
   1824         public String receivedInState;
   1825 
   1826         public void reset() {
   1827             processedInState = null;
   1828             receivedInState = null;
   1829         }
   1830 
   1831         public void handled(State processedIn, IState receivedIn) {
   1832             processedInState = processedIn.getClass().getSimpleName();
   1833             receivedInState = receivedIn.getName();
   1834         }
   1835 
   1836         public String toString() {
   1837             return String.format("rcvd_in=%s, proc_in=%s",
   1838                                  receivedInState, processedInState);
   1839         }
   1840     }
   1841 
   1842     // TODO: extract out into CollectionUtils.
   1843     static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
   1844         for (T t : coll) {
   1845             if (fn.test(t)) {
   1846                 return true;
   1847             }
   1848         }
   1849         return false;
   1850     }
   1851 
   1852     static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
   1853         return !any(coll, not(fn));
   1854     }
   1855 
   1856     static <T> Predicate<T> not(Predicate<T> fn) {
   1857         return (t) -> !fn.test(t);
   1858     }
   1859 
   1860     static <T> String join(String delimiter, Collection<T> coll) {
   1861         return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter));
   1862     }
   1863 
   1864     static <T> T find(Iterable<T> coll, Predicate<T> fn) {
   1865         for (T t: coll) {
   1866             if (fn.test(t)) {
   1867               return t;
   1868             }
   1869         }
   1870         return null;
   1871     }
   1872 
   1873     static <T> List<T> findAll(Collection<T> coll, Predicate<T> fn) {
   1874         return coll.stream().filter(fn).collect(Collectors.toList());
   1875     }
   1876 }
   1877