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 static android.net.RouteInfo.RTN_UNICAST;
20 import static android.net.dhcp.DhcpResultsParcelableUtil.toStableParcelable;
21 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
22 
23 import static com.android.server.util.NetworkStackConstants.VENDOR_SPECIFIC_IE_ID;
24 import static com.android.server.util.PermissionUtil.enforceNetworkStackCallingPermission;
25 
26 import android.content.Context;
27 import android.net.ConnectivityManager;
28 import android.net.DhcpResults;
29 import android.net.INetd;
30 import android.net.IpPrefix;
31 import android.net.Layer2InformationParcelable;
32 import android.net.Layer2PacketParcelable;
33 import android.net.LinkAddress;
34 import android.net.LinkProperties;
35 import android.net.MacAddress;
36 import android.net.NattKeepalivePacketDataParcelable;
37 import android.net.NetworkStackIpMemoryStore;
38 import android.net.ProvisioningConfigurationParcelable;
39 import android.net.ProxyInfo;
40 import android.net.RouteInfo;
41 import android.net.TcpKeepalivePacketDataParcelable;
42 import android.net.Uri;
43 import android.net.apf.ApfCapabilities;
44 import android.net.apf.ApfFilter;
45 import android.net.dhcp.DhcpClient;
46 import android.net.dhcp.DhcpPacket;
47 import android.net.metrics.IpConnectivityLog;
48 import android.net.metrics.IpManagerEvent;
49 import android.net.shared.InitialConfiguration;
50 import android.net.shared.ProvisioningConfiguration;
51 import android.net.shared.ProvisioningConfiguration.ScanResultInfo;
52 import android.net.shared.ProvisioningConfiguration.ScanResultInfo.InformationElement;
53 import android.net.util.InterfaceParams;
54 import android.net.util.NetworkStackUtils;
55 import android.net.util.SharedLog;
56 import android.os.Build;
57 import android.os.ConditionVariable;
58 import android.os.IBinder;
59 import android.os.Message;
60 import android.os.RemoteException;
61 import android.os.ServiceSpecificException;
62 import android.os.SystemClock;
63 import android.stats.connectivity.DisconnectCode;
64 import android.text.TextUtils;
65 import android.util.LocalLog;
66 import android.util.Log;
67 import android.util.Pair;
68 import android.util.SparseArray;
69 
70 import androidx.annotation.NonNull;
71 
72 import com.android.internal.annotations.VisibleForTesting;
73 import com.android.internal.util.HexDump;
74 import com.android.internal.util.IState;
75 import com.android.internal.util.IndentingPrintWriter;
76 import com.android.internal.util.MessageUtils;
77 import com.android.internal.util.State;
78 import com.android.internal.util.StateMachine;
79 import com.android.internal.util.WakeupMessage;
80 import com.android.networkstack.apishim.NetworkInformationShimImpl;
81 import com.android.networkstack.apishim.common.NetworkInformationShim;
82 import com.android.networkstack.apishim.common.ShimUtils;
83 import com.android.networkstack.metrics.IpProvisioningMetrics;
84 import com.android.server.NetworkObserverRegistry;
85 import com.android.server.NetworkStackService.NetworkStackServiceManager;
86 
87 import java.io.FileDescriptor;
88 import java.io.PrintWriter;
89 import java.net.InetAddress;
90 import java.net.MalformedURLException;
91 import java.net.URL;
92 import java.nio.BufferUnderflowException;
93 import java.nio.ByteBuffer;
94 import java.util.ArrayList;
95 import java.util.Arrays;
96 import java.util.Collection;
97 import java.util.Collections;
98 import java.util.HashSet;
99 import java.util.List;
100 import java.util.Objects;
101 import java.util.Set;
102 import java.util.concurrent.ConcurrentHashMap;
103 import java.util.concurrent.CountDownLatch;
104 import java.util.function.Predicate;
105 import java.util.stream.Collectors;
106 
107 /**
108  * IpClient
109  *
110  * This class provides the interface to IP-layer provisioning and maintenance
111  * functionality that can be used by transport layers like Wi-Fi, Ethernet,
112  * et cetera.
113  *
114  * [ Lifetime ]
115  * IpClient is designed to be instantiated as soon as the interface name is
116  * known and can be as long-lived as the class containing it (i.e. declaring
117  * it "private final" is okay).
118  *
119  * @hide
120  */
121 public class IpClient extends StateMachine {
122     private static final boolean DBG = false;
123 
124     // For message logging.
125     private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class };
126     private static final SparseArray<String> sWhatToString =
127             MessageUtils.findMessageNames(sMessageClasses);
128     // Two static concurrent hashmaps of interface name to logging classes.
129     // One holds StateMachine logs and the other connectivity packet logs.
130     private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>();
131     private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>();
132     private final NetworkStackIpMemoryStore mIpMemoryStore;
133     private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance();
134     private final IpProvisioningMetrics mIpProvisioningMetrics = new IpProvisioningMetrics();
135 
136     /**
137      * Dump all state machine and connectivity packet logs to the specified writer.
138      * @param skippedIfaces Interfaces for which logs should not be dumped.
139      */
dumpAllLogs(PrintWriter writer, Set<String> skippedIfaces)140     public static void dumpAllLogs(PrintWriter writer, Set<String> skippedIfaces) {
141         for (String ifname : sSmLogs.keySet()) {
142             if (skippedIfaces.contains(ifname)) continue;
143 
144             writer.println(String.format("--- BEGIN %s ---", ifname));
145 
146             final SharedLog smLog = sSmLogs.get(ifname);
147             if (smLog != null) {
148                 writer.println("State machine log:");
149                 smLog.dump(null, writer, null);
150             }
151 
152             writer.println("");
153 
154             final LocalLog pktLog = sPktLogs.get(ifname);
155             if (pktLog != null) {
156                 writer.println("Connectivity packet log:");
157                 pktLog.readOnlyLocalLog().dump(null, writer, null);
158             }
159 
160             writer.println(String.format("--- END %s ---", ifname));
161         }
162     }
163 
164     // Use a wrapper class to log in order to ensure complete and detailed
165     // logging. This method is lighter weight than annotations/reflection
166     // and has the following benefits:
167     //
168     //     - No invoked method can be forgotten.
169     //       Any new method added to IpClient.Callback must be overridden
170     //       here or it will never be called.
171     //
172     //     - No invoking call site can be forgotten.
173     //       Centralized logging in this way means call sites don't need to
174     //       remember to log, and therefore no call site can be forgotten.
175     //
176     //     - No variation in log format among call sites.
177     //       Encourages logging of any available arguments, and all call sites
178     //       are necessarily logged identically.
179     //
180     // NOTE: Log first because passed objects may or may not be thread-safe and
181     // once passed on to the callback they may be modified by another thread.
182     //
183     // TODO: Find an lighter weight approach.
184     public static class IpClientCallbacksWrapper {
185         private static final String PREFIX = "INVOKE ";
186         private final IIpClientCallbacks mCallback;
187         private final SharedLog mLog;
188         @NonNull
189         private final NetworkInformationShim mShim;
190 
191         @VisibleForTesting
IpClientCallbacksWrapper(IIpClientCallbacks callback, SharedLog log, @NonNull NetworkInformationShim shim)192         protected IpClientCallbacksWrapper(IIpClientCallbacks callback, SharedLog log,
193                 @NonNull NetworkInformationShim shim) {
194             mCallback = callback;
195             mLog = log;
196             mShim = shim;
197         }
198 
log(String msg)199         private void log(String msg) {
200             mLog.log(PREFIX + msg);
201         }
202 
log(String msg, Throwable e)203         private void log(String msg, Throwable e) {
204             mLog.e(PREFIX + msg, e);
205         }
206 
207         /**
208          * Callback called prior to DHCP discovery/renewal only if the pre DHCP action
209          * is enabled.
210          */
onPreDhcpAction()211         public void onPreDhcpAction() {
212             log("onPreDhcpAction()");
213             try {
214                 mCallback.onPreDhcpAction();
215             } catch (RemoteException e) {
216                 log("Failed to call onPreDhcpAction", e);
217             }
218         }
219 
220         /**
221          * Callback called after DHCP discovery/renewal only if the pre DHCP action
222          * is enabled.
223          */
onPostDhcpAction()224         public void onPostDhcpAction() {
225             log("onPostDhcpAction()");
226             try {
227                 mCallback.onPostDhcpAction();
228             } catch (RemoteException e) {
229                 log("Failed to call onPostDhcpAction", e);
230             }
231         }
232 
233         /**
234          * Callback called when new DHCP results are available.
235          */
onNewDhcpResults(DhcpResults dhcpResults)236         public void onNewDhcpResults(DhcpResults dhcpResults) {
237             log("onNewDhcpResults({" + dhcpResults + "})");
238             try {
239                 mCallback.onNewDhcpResults(toStableParcelable(dhcpResults));
240             } catch (RemoteException e) {
241                 log("Failed to call onNewDhcpResults", e);
242             }
243         }
244 
245         /**
246          * Indicates that provisioning was successful.
247          */
onProvisioningSuccess(LinkProperties newLp)248         public void onProvisioningSuccess(LinkProperties newLp) {
249             log("onProvisioningSuccess({" + newLp + "})");
250             try {
251                 mCallback.onProvisioningSuccess(mShim.makeSensitiveFieldsParcelingCopy(newLp));
252             } catch (RemoteException e) {
253                 log("Failed to call onProvisioningSuccess", e);
254             }
255         }
256 
257         /**
258          * Indicates that provisioning failed.
259          */
onProvisioningFailure(LinkProperties newLp)260         public void onProvisioningFailure(LinkProperties newLp) {
261             log("onProvisioningFailure({" + newLp + "})");
262             try {
263                 mCallback.onProvisioningFailure(mShim.makeSensitiveFieldsParcelingCopy(newLp));
264             } catch (RemoteException e) {
265                 log("Failed to call onProvisioningFailure", e);
266             }
267         }
268 
269         /**
270          * Invoked on LinkProperties changes.
271          */
onLinkPropertiesChange(LinkProperties newLp)272         public void onLinkPropertiesChange(LinkProperties newLp) {
273             log("onLinkPropertiesChange({" + newLp + "})");
274             try {
275                 mCallback.onLinkPropertiesChange(mShim.makeSensitiveFieldsParcelingCopy(newLp));
276             } catch (RemoteException e) {
277                 log("Failed to call onLinkPropertiesChange", e);
278             }
279         }
280 
281         /**
282          * Called when the internal IpReachabilityMonitor (if enabled) has detected the loss of
283          * required neighbors (e.g. on-link default gw or dns servers) due to NUD_FAILED.
284          */
onReachabilityLost(String logMsg)285         public void onReachabilityLost(String logMsg) {
286             log("onReachabilityLost(" + logMsg + ")");
287             try {
288                 mCallback.onReachabilityLost(logMsg);
289             } catch (RemoteException e) {
290                 log("Failed to call onReachabilityLost", e);
291             }
292         }
293 
294         /**
295          * Called when the IpClient state machine terminates.
296          */
onQuit()297         public void onQuit() {
298             log("onQuit()");
299             try {
300                 mCallback.onQuit();
301             } catch (RemoteException e) {
302                 log("Failed to call onQuit", e);
303             }
304         }
305 
306         /**
307          * Called to indicate that a new APF program must be installed to filter incoming packets.
308          */
installPacketFilter(byte[] filter)309         public void installPacketFilter(byte[] filter) {
310             log("installPacketFilter(byte[" + filter.length + "])");
311             try {
312                 mCallback.installPacketFilter(filter);
313             } catch (RemoteException e) {
314                 log("Failed to call installPacketFilter", e);
315             }
316         }
317 
318         /**
319          * Called to indicate that the APF Program & data buffer must be read asynchronously from
320          * the wifi driver.
321          */
startReadPacketFilter()322         public void startReadPacketFilter() {
323             log("startReadPacketFilter()");
324             try {
325                 mCallback.startReadPacketFilter();
326             } catch (RemoteException e) {
327                 log("Failed to call startReadPacketFilter", e);
328             }
329         }
330 
331         /**
332          * If multicast filtering cannot be accomplished with APF, this function will be called to
333          * actuate multicast filtering using another means.
334          */
setFallbackMulticastFilter(boolean enabled)335         public void setFallbackMulticastFilter(boolean enabled) {
336             log("setFallbackMulticastFilter(" + enabled + ")");
337             try {
338                 mCallback.setFallbackMulticastFilter(enabled);
339             } catch (RemoteException e) {
340                 log("Failed to call setFallbackMulticastFilter", e);
341             }
342         }
343 
344         /**
345          * Enabled/disable Neighbor Discover offload functionality. This is called, for example,
346          * whenever 464xlat is being started or stopped.
347          */
setNeighborDiscoveryOffload(boolean enable)348         public void setNeighborDiscoveryOffload(boolean enable) {
349             log("setNeighborDiscoveryOffload(" + enable + ")");
350             try {
351                 mCallback.setNeighborDiscoveryOffload(enable);
352             } catch (RemoteException e) {
353                 log("Failed to call setNeighborDiscoveryOffload", e);
354             }
355         }
356 
357         /**
358          * Invoked on starting preconnection process.
359          */
onPreconnectionStart(List<Layer2PacketParcelable> packets)360         public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {
361             log("onPreconnectionStart(Layer2Packets[" + packets.size() + "])");
362             try {
363                 mCallback.onPreconnectionStart(packets);
364             } catch (RemoteException e) {
365                 log("Failed to call onPreconnectionStart", e);
366             }
367         }
368     }
369 
370     public static final String DUMP_ARG_CONFIRM = "confirm";
371 
372     // Below constants are picked up by MessageUtils and exempt from ProGuard optimization.
373     private static final int CMD_TERMINATE_AFTER_STOP             = 1;
374     private static final int CMD_STOP                             = 2;
375     private static final int CMD_START                            = 3;
376     private static final int CMD_CONFIRM                          = 4;
377     private static final int EVENT_PRE_DHCP_ACTION_COMPLETE       = 5;
378     // Triggered by NetlinkTracker to communicate netlink events.
379     private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6;
380     private static final int CMD_UPDATE_TCP_BUFFER_SIZES          = 7;
381     private static final int CMD_UPDATE_HTTP_PROXY                = 8;
382     private static final int CMD_SET_MULTICAST_FILTER             = 9;
383     private static final int EVENT_PROVISIONING_TIMEOUT           = 10;
384     private static final int EVENT_DHCPACTION_TIMEOUT             = 11;
385     private static final int EVENT_READ_PACKET_FILTER_COMPLETE    = 12;
386     private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13;
387     private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14;
388     private static final int CMD_UPDATE_L2KEY_CLUSTER = 15;
389     private static final int CMD_COMPLETE_PRECONNECTION = 16;
390     private static final int CMD_UPDATE_L2INFORMATION = 17;
391 
392     private static final int ARG_LINKPROP_CHANGED_LINKSTATE_DOWN = 0;
393     private static final int ARG_LINKPROP_CHANGED_LINKSTATE_UP = 1;
394 
395     // Internal commands to use instead of trying to call transitionTo() inside
396     // a given State's enter() method. Calling transitionTo() from enter/exit
397     // encounters a Log.wtf() that can cause trouble on eng builds.
398     private static final int CMD_ADDRESSES_CLEARED                = 100;
399     private static final int CMD_JUMP_RUNNING_TO_STOPPING         = 101;
400     private static final int CMD_JUMP_STOPPING_TO_STOPPED         = 102;
401 
402     // IpClient shares a handler with DhcpClient: commands must not overlap
403     public static final int DHCPCLIENT_CMD_BASE = 1000;
404 
405     // Settings and default values.
406     private static final int MAX_LOG_RECORDS = 500;
407     private static final int MAX_PACKET_RECORDS = 100;
408 
409     @VisibleForTesting
410     static final String CONFIG_MIN_RDNSS_LIFETIME = "ipclient_min_rdnss_lifetime";
411     private static final int DEFAULT_MIN_RDNSS_LIFETIME =
412             ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q) ? 120 : 0;
413 
414     private static final boolean NO_CALLBACKS = false;
415     private static final boolean SEND_CALLBACKS = true;
416 
417     // This must match the interface prefix in clatd.c.
418     // TODO: Revert this hack once IpClient and Nat464Xlat work in concert.
419     private static final String CLAT_PREFIX = "v4-";
420 
421     private static final int IMMEDIATE_FAILURE_DURATION = 0;
422 
423     private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1;
424     private static final int PROV_CHANGE_LOST_PROVISIONING = 2;
425     private static final int PROV_CHANGE_GAINED_PROVISIONING = 3;
426     private static final int PROV_CHANGE_STILL_PROVISIONED = 4;
427 
428     // Specific vendor OUI(3 bytes)/vendor specific type(1 byte) pattern for upstream hotspot
429     // device detection. Add new byte array pattern below in turn.
430     private static final List<byte[]> METERED_IE_PATTERN_LIST = Collections.unmodifiableList(
431             Arrays.asList(
432                     new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xf2, (byte) 0x06 }
433     ));
434 
435     // Initialize configurable particular SSID set supporting DHCP Roaming feature. See
436     // b/131797393 for more details.
437     private static final Set<String> DHCP_ROAMING_SSID_SET = new HashSet<>(
438             Arrays.asList(
439                     "0001docomo", "ollehWiFi", "olleh GiGa WiFi", "KT WiFi",
440                     "KT GiGA WiFi", "marente"
441     ));
442 
443     private final State mStoppedState = new StoppedState();
444     private final State mStoppingState = new StoppingState();
445     private final State mClearingIpAddressesState = new ClearingIpAddressesState();
446     private final State mStartedState = new StartedState();
447     private final State mRunningState = new RunningState();
448     private final State mPreconnectingState = new PreconnectingState();
449 
450     private final String mTag;
451     private final Context mContext;
452     private final String mInterfaceName;
453     private final String mClatInterfaceName;
454     @VisibleForTesting
455     protected final IpClientCallbacksWrapper mCallback;
456     private final Dependencies mDependencies;
457     private final CountDownLatch mShutdownLatch;
458     private final ConnectivityManager mCm;
459     private final INetd mNetd;
460     private final NetworkObserverRegistry mObserverRegistry;
461     private final IpClientLinkObserver mLinkObserver;
462     private final WakeupMessage mProvisioningTimeoutAlarm;
463     private final WakeupMessage mDhcpActionTimeoutAlarm;
464     private final SharedLog mLog;
465     private final LocalLog mConnectivityPacketLog;
466     private final MessageHandlingLogger mMsgStateLogger;
467     private final IpConnectivityLog mMetricsLog;
468     private final InterfaceController mInterfaceCtrl;
469 
470     // Ignore nonzero RDNSS option lifetimes below this value. 0 = disabled.
471     private final int mMinRdnssLifetimeSec;
472 
473     private InterfaceParams mInterfaceParams;
474 
475     /**
476      * Non-final member variables accessed only from within our StateMachine.
477      */
478     private LinkProperties mLinkProperties;
479     private android.net.shared.ProvisioningConfiguration mConfiguration;
480     private IpReachabilityMonitor mIpReachabilityMonitor;
481     private DhcpClient mDhcpClient;
482     private DhcpResults mDhcpResults;
483     private String mTcpBufferSizes;
484     private ProxyInfo mHttpProxy;
485     private ApfFilter mApfFilter;
486     private String mL2Key; // The L2 key for this network, for writing into the memory store
487     private String mCluster; // The cluster for this network, for writing into the memory store
488     private boolean mMulticastFiltering;
489     private long mStartTimeMillis;
490     private MacAddress mCurrentBssid;
491     private boolean mHasDisabledIPv6OnProvLoss;
492 
493     /**
494      * Reading the snapshot is an asynchronous operation initiated by invoking
495      * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
496      * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable
497      * signals when a new snapshot is ready.
498      */
499     private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable();
500 
501     public static class Dependencies {
502         /**
503          * Get interface parameters for the specified interface.
504          */
getInterfaceParams(String ifname)505         public InterfaceParams getInterfaceParams(String ifname) {
506             return InterfaceParams.getByName(ifname);
507         }
508 
509         /**
510          * Get a INetd connector.
511          */
getNetd(Context context)512         public INetd getNetd(Context context) {
513             return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
514         }
515 
516         /**
517          * Get a IpMemoryStore instance.
518          */
getIpMemoryStore(Context context, NetworkStackServiceManager nssManager)519         public NetworkStackIpMemoryStore getIpMemoryStore(Context context,
520                 NetworkStackServiceManager nssManager) {
521             return new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService());
522         }
523 
524         /**
525          * Get a DhcpClient instance.
526          */
makeDhcpClient(Context context, StateMachine controller, InterfaceParams ifParams, DhcpClient.Dependencies deps)527         public DhcpClient makeDhcpClient(Context context, StateMachine controller,
528                 InterfaceParams ifParams, DhcpClient.Dependencies deps) {
529             return DhcpClient.makeDhcpClient(context, controller, ifParams, deps);
530         }
531 
532         /**
533          * Get a DhcpClient Dependencies instance.
534          */
getDhcpClientDependencies( NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics)535         public DhcpClient.Dependencies getDhcpClientDependencies(
536                 NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) {
537             return new DhcpClient.Dependencies(ipMemoryStore, metrics);
538         }
539 
540         /**
541          * Read an integer DeviceConfig property.
542          */
getDeviceConfigPropertyInt(String name, int defaultValue)543         public int getDeviceConfigPropertyInt(String name, int defaultValue) {
544             return NetworkStackUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, name,
545                     defaultValue);
546         }
547 
548         /**
549          * Get a IpConnectivityLog instance.
550          */
getIpConnectivityLog()551         public IpConnectivityLog getIpConnectivityLog() {
552             return new IpConnectivityLog();
553         }
554     }
555 
IpClient(Context context, String ifName, IIpClientCallbacks callback, NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager)556     public IpClient(Context context, String ifName, IIpClientCallbacks callback,
557             NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager) {
558         this(context, ifName, callback, observerRegistry, nssManager, new Dependencies());
559     }
560 
561     @VisibleForTesting
IpClient(Context context, String ifName, IIpClientCallbacks callback, NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager, Dependencies deps)562     IpClient(Context context, String ifName, IIpClientCallbacks callback,
563             NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager,
564             Dependencies deps) {
565         super(IpClient.class.getSimpleName() + "." + ifName);
566         Objects.requireNonNull(ifName);
567         Objects.requireNonNull(callback);
568 
569         mTag = getName();
570 
571         mContext = context;
572         mInterfaceName = ifName;
573         mClatInterfaceName = CLAT_PREFIX + ifName;
574         mDependencies = deps;
575         mMetricsLog = deps.getIpConnectivityLog();
576         mShutdownLatch = new CountDownLatch(1);
577         mCm = mContext.getSystemService(ConnectivityManager.class);
578         mObserverRegistry = observerRegistry;
579         mIpMemoryStore = deps.getIpMemoryStore(context, nssManager);
580 
581         sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
582         mLog = sSmLogs.get(mInterfaceName);
583         sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
584         mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
585         mMsgStateLogger = new MessageHandlingLogger();
586         mCallback = new IpClientCallbacksWrapper(callback, mLog, mShim);
587 
588         // TODO: Consider creating, constructing, and passing in some kind of
589         // InterfaceController.Dependencies class.
590         mNetd = deps.getNetd(mContext);
591         mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog);
592 
593         mMinRdnssLifetimeSec = mDependencies.getDeviceConfigPropertyInt(
594                 CONFIG_MIN_RDNSS_LIFETIME, DEFAULT_MIN_RDNSS_LIFETIME);
595 
596         IpClientLinkObserver.Configuration config = new IpClientLinkObserver.Configuration(
597                 mMinRdnssLifetimeSec);
598 
599         mLinkObserver = new IpClientLinkObserver(
600                 mContext, getHandler(),
601                 mInterfaceName,
602                 (ifaceUp) -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED, ifaceUp
603                         ? ARG_LINKPROP_CHANGED_LINKSTATE_UP
604                         : ARG_LINKPROP_CHANGED_LINKSTATE_DOWN),
605                 config, mLog) {
606             @Override
607             public void onInterfaceAdded(String iface) {
608                 super.onInterfaceAdded(iface);
609                 if (mClatInterfaceName.equals(iface)) {
610                     mCallback.setNeighborDiscoveryOffload(false);
611                 } else if (!mInterfaceName.equals(iface)) {
612                     return;
613                 }
614 
615                 final String msg = "interfaceAdded(" + iface + ")";
616                 logMsg(msg);
617             }
618 
619             @Override
620             public void onInterfaceRemoved(String iface) {
621                 super.onInterfaceRemoved(iface);
622                 // TODO: Also observe mInterfaceName going down and take some
623                 // kind of appropriate action.
624                 if (mClatInterfaceName.equals(iface)) {
625                     // TODO: consider sending a message to the IpClient main
626                     // StateMachine thread, in case "NDO enabled" state becomes
627                     // tied to more things that 464xlat operation.
628                     mCallback.setNeighborDiscoveryOffload(true);
629                 } else if (!mInterfaceName.equals(iface)) {
630                     return;
631                 }
632 
633                 final String msg = "interfaceRemoved(" + iface + ")";
634                 logMsg(msg);
635             }
636 
637             private void logMsg(String msg) {
638                 Log.d(mTag, msg);
639                 getHandler().post(() -> mLog.log("OBSERVED " + msg));
640             }
641         };
642 
643         mLinkProperties = new LinkProperties();
644         mLinkProperties.setInterfaceName(mInterfaceName);
645 
646         mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
647                 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
648         mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
649                 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
650 
651         // Anything the StateMachine may access must have been instantiated
652         // before this point.
653         configureAndStartStateMachine();
654 
655         // Anything that may send messages to the StateMachine must only be
656         // configured to do so after the StateMachine has started (above).
657         startStateMachineUpdaters();
658     }
659 
660     /**
661      * Make a IIpClient connector to communicate with this IpClient.
662      */
makeConnector()663     public IIpClient makeConnector() {
664         return new IpClientConnector();
665     }
666 
667     class IpClientConnector extends IIpClient.Stub {
668         @Override
completedPreDhcpAction()669         public void completedPreDhcpAction() {
670             enforceNetworkStackCallingPermission();
671             IpClient.this.completedPreDhcpAction();
672         }
673         @Override
confirmConfiguration()674         public void confirmConfiguration() {
675             enforceNetworkStackCallingPermission();
676             IpClient.this.confirmConfiguration();
677         }
678         @Override
readPacketFilterComplete(byte[] data)679         public void readPacketFilterComplete(byte[] data) {
680             enforceNetworkStackCallingPermission();
681             IpClient.this.readPacketFilterComplete(data);
682         }
683         @Override
shutdown()684         public void shutdown() {
685             enforceNetworkStackCallingPermission();
686             IpClient.this.shutdown();
687         }
688         @Override
startProvisioning(ProvisioningConfigurationParcelable req)689         public void startProvisioning(ProvisioningConfigurationParcelable req) {
690             enforceNetworkStackCallingPermission();
691             IpClient.this.startProvisioning(ProvisioningConfiguration.fromStableParcelable(req));
692         }
693         @Override
stop()694         public void stop() {
695             enforceNetworkStackCallingPermission();
696             IpClient.this.stop();
697         }
698         @Override
setL2KeyAndGroupHint(String l2Key, String cluster)699         public void setL2KeyAndGroupHint(String l2Key, String cluster) {
700             enforceNetworkStackCallingPermission();
701             IpClient.this.setL2KeyAndCluster(l2Key, cluster);
702         }
703         @Override
setTcpBufferSizes(String tcpBufferSizes)704         public void setTcpBufferSizes(String tcpBufferSizes) {
705             enforceNetworkStackCallingPermission();
706             IpClient.this.setTcpBufferSizes(tcpBufferSizes);
707         }
708         @Override
setHttpProxy(ProxyInfo proxyInfo)709         public void setHttpProxy(ProxyInfo proxyInfo) {
710             enforceNetworkStackCallingPermission();
711             IpClient.this.setHttpProxy(proxyInfo);
712         }
713         @Override
setMulticastFilter(boolean enabled)714         public void setMulticastFilter(boolean enabled) {
715             enforceNetworkStackCallingPermission();
716             IpClient.this.setMulticastFilter(enabled);
717         }
718         @Override
addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt)719         public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
720             enforceNetworkStackCallingPermission();
721             IpClient.this.addKeepalivePacketFilter(slot, pkt);
722         }
723         @Override
addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt)724         public void addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt) {
725             enforceNetworkStackCallingPermission();
726             IpClient.this.addNattKeepalivePacketFilter(slot, pkt);
727         }
728         @Override
removeKeepalivePacketFilter(int slot)729         public void removeKeepalivePacketFilter(int slot) {
730             enforceNetworkStackCallingPermission();
731             IpClient.this.removeKeepalivePacketFilter(slot);
732         }
733         @Override
notifyPreconnectionComplete(boolean success)734         public void notifyPreconnectionComplete(boolean success) {
735             enforceNetworkStackCallingPermission();
736             IpClient.this.notifyPreconnectionComplete(success);
737         }
738         @Override
updateLayer2Information(Layer2InformationParcelable info)739         public void updateLayer2Information(Layer2InformationParcelable info) {
740             enforceNetworkStackCallingPermission();
741             IpClient.this.updateLayer2Information(info);
742         }
743 
744         @Override
getInterfaceVersion()745         public int getInterfaceVersion() {
746             return this.VERSION;
747         }
748 
749         @Override
getInterfaceHash()750         public String getInterfaceHash() {
751             return this.HASH;
752         }
753     }
754 
getInterfaceName()755     public String getInterfaceName() {
756         return mInterfaceName;
757     }
758 
configureAndStartStateMachine()759     private void configureAndStartStateMachine() {
760         // CHECKSTYLE:OFF IndentationCheck
761         addState(mStoppedState);
762         addState(mStartedState);
763             addState(mPreconnectingState, mStartedState);
764             addState(mClearingIpAddressesState, mStartedState);
765             addState(mRunningState, mStartedState);
766         addState(mStoppingState);
767         // CHECKSTYLE:ON IndentationCheck
768 
769         setInitialState(mStoppedState);
770 
771         super.start();
772     }
773 
startStateMachineUpdaters()774     private void startStateMachineUpdaters() {
775         mObserverRegistry.registerObserverForNonblockingCallback(mLinkObserver);
776     }
777 
stopStateMachineUpdaters()778     private void stopStateMachineUpdaters() {
779         mObserverRegistry.unregisterObserver(mLinkObserver);
780     }
781 
782     @Override
onQuitting()783     protected void onQuitting() {
784         mCallback.onQuit();
785         mShutdownLatch.countDown();
786     }
787 
788     /**
789      * Shut down this IpClient instance altogether.
790      */
shutdown()791     public void shutdown() {
792         stop();
793         sendMessage(CMD_TERMINATE_AFTER_STOP);
794     }
795 
796     /**
797      * Start provisioning with the provided parameters.
798      */
startProvisioning(ProvisioningConfiguration req)799     public void startProvisioning(ProvisioningConfiguration req) {
800         if (!req.isValid()) {
801             doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
802             return;
803         }
804 
805         final ScanResultInfo scanResultInfo = req.mScanResultInfo;
806         mCurrentBssid = null;
807         if (scanResultInfo != null) {
808             try {
809                 mCurrentBssid = MacAddress.fromString(scanResultInfo.getBssid());
810             } catch (IllegalArgumentException e) {
811                 Log.wtf(mTag, "Invalid BSSID: " + scanResultInfo.getBssid()
812                         + " in provisioning configuration", e);
813             }
814         }
815 
816         if (req.mLayer2Info != null) {
817             mL2Key = req.mLayer2Info.mL2Key;
818             mCluster = req.mLayer2Info.mCluster;
819         }
820         sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
821     }
822 
823     /**
824      * Stop this IpClient.
825      *
826      * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
827      *    The message "arg1" parameter is used to record the disconnect code metrics.
828      *    Usually this method is called by the peer (e.g. wifi) intentionally to stop IpClient,
829      *    consider that's the normal user termination.
830      */
stop()831     public void stop() {
832         sendMessage(CMD_STOP, DisconnectCode.DC_NORMAL_TERMINATION.getNumber());
833     }
834 
835     /**
836      * Confirm the provisioning configuration.
837      */
confirmConfiguration()838     public void confirmConfiguration() {
839         sendMessage(CMD_CONFIRM);
840     }
841 
842     /**
843      * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be
844      * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to
845      * proceed.
846      */
completedPreDhcpAction()847     public void completedPreDhcpAction() {
848         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
849     }
850 
851     /**
852      * Indicate that packet filter read is complete.
853      */
readPacketFilterComplete(byte[] data)854     public void readPacketFilterComplete(byte[] data) {
855         sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
856     }
857 
858     /**
859      * Set the TCP buffer sizes to use.
860      *
861      * This may be called, repeatedly, at any time before or after a call to
862      * #startProvisioning(). The setting is cleared upon calling #stop().
863      */
setTcpBufferSizes(String tcpBufferSizes)864     public void setTcpBufferSizes(String tcpBufferSizes) {
865         sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
866     }
867 
868     /**
869      * Set the L2 key and cluster for storing info into the memory store.
870      *
871      * This method is only supported on Q devices. For R or above releases,
872      * caller should call #updateLayer2Information() instead.
873      */
setL2KeyAndCluster(String l2Key, String cluster)874     public void setL2KeyAndCluster(String l2Key, String cluster) {
875         if (!ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)) {
876             sendMessage(CMD_UPDATE_L2KEY_CLUSTER, new Pair<>(l2Key, cluster));
877         }
878     }
879 
880     /**
881      * Set the HTTP Proxy configuration to use.
882      *
883      * This may be called, repeatedly, at any time before or after a call to
884      * #startProvisioning(). The setting is cleared upon calling #stop().
885      */
setHttpProxy(ProxyInfo proxyInfo)886     public void setHttpProxy(ProxyInfo proxyInfo) {
887         sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
888     }
889 
890     /**
891      * Enable or disable the multicast filter.  Attempts to use APF to accomplish the filtering,
892      * if not, Callback.setFallbackMulticastFilter() is called.
893      */
setMulticastFilter(boolean enabled)894     public void setMulticastFilter(boolean enabled) {
895         sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
896     }
897 
898     /**
899      * Called by WifiStateMachine to add TCP keepalive packet filter before setting up
900      * keepalive offload.
901      */
addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt)902     public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) {
903         sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt);
904     }
905 
906     /**
907      *  Called by WifiStateMachine to add NATT keepalive packet filter before setting up
908      *  keepalive offload.
909      */
addNattKeepalivePacketFilter(int slot, @NonNull NattKeepalivePacketDataParcelable pkt)910     public void addNattKeepalivePacketFilter(int slot,
911             @NonNull NattKeepalivePacketDataParcelable pkt) {
912         sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */ , pkt);
913     }
914 
915     /**
916      * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive
917      * offload.
918      */
removeKeepalivePacketFilter(int slot)919     public void removeKeepalivePacketFilter(int slot) {
920         sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */);
921     }
922 
923     /**
924      * Notify IpClient that preconnection is complete and that the link is ready for use.
925      * The success parameter indicates whether the packets passed in by onPreconnectionStart were
926      * successfully sent to the network or not.
927      */
notifyPreconnectionComplete(boolean success)928     public void notifyPreconnectionComplete(boolean success) {
929         sendMessage(CMD_COMPLETE_PRECONNECTION, success ? 1 : 0);
930     }
931 
932     /**
933      * Update the network bssid, L2Key and cluster on L2 roaming happened.
934      */
updateLayer2Information(@onNull Layer2InformationParcelable info)935     public void updateLayer2Information(@NonNull Layer2InformationParcelable info) {
936         sendMessage(CMD_UPDATE_L2INFORMATION, info);
937     }
938 
939     /**
940      * Dump logs of this IpClient.
941      */
dump(FileDescriptor fd, PrintWriter writer, String[] args)942     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
943         if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
944             // Execute confirmConfiguration() and take no further action.
945             confirmConfiguration();
946             return;
947         }
948 
949         // Thread-unsafe access to mApfFilter but just used for debugging.
950         final ApfFilter apfFilter = mApfFilter;
951         final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration;
952         final ApfCapabilities apfCapabilities = (provisioningConfig != null)
953                 ? provisioningConfig.mApfCapabilities : null;
954 
955         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
956         pw.println(mTag + " APF dump:");
957         pw.increaseIndent();
958         if (apfFilter != null) {
959             if (apfCapabilities.hasDataAccess()) {
960                 // Request a new snapshot, then wait for it.
961                 mApfDataSnapshotComplete.close();
962                 mCallback.startReadPacketFilter();
963                 if (!mApfDataSnapshotComplete.block(1000)) {
964                     pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT");
965                 }
966             }
967             apfFilter.dump(pw);
968 
969         } else {
970             pw.print("No active ApfFilter; ");
971             if (provisioningConfig == null) {
972                 pw.println("IpClient not yet started.");
973             } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) {
974                 pw.println("Hardware does not support APF.");
975             } else {
976                 pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities);
977             }
978         }
979         pw.decreaseIndent();
980         pw.println();
981         pw.println(mTag + " current ProvisioningConfiguration:");
982         pw.increaseIndent();
983         pw.println(Objects.toString(provisioningConfig, "N/A"));
984         pw.decreaseIndent();
985 
986         final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
987         if (iprm != null) {
988             pw.println();
989             pw.println(mTag + " current IpReachabilityMonitor state:");
990             pw.increaseIndent();
991             iprm.dump(pw);
992             pw.decreaseIndent();
993         }
994 
995         pw.println();
996         pw.println(mTag + " StateMachine dump:");
997         pw.increaseIndent();
998         mLog.dump(fd, pw, args);
999         pw.decreaseIndent();
1000 
1001         pw.println();
1002         pw.println(mTag + " connectivity packet log:");
1003         pw.println();
1004         pw.println("Debug with python and scapy via:");
1005         pw.println("shell$ python");
1006         pw.println(">>> from scapy import all as scapy");
1007         pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()");
1008         pw.println();
1009 
1010         pw.increaseIndent();
1011         mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
1012         pw.decreaseIndent();
1013     }
1014 
1015 
1016     /**
1017      * Internals.
1018      */
1019 
1020     @Override
getWhatToString(int what)1021     protected String getWhatToString(int what) {
1022         return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
1023     }
1024 
1025     @Override
getLogRecString(Message msg)1026     protected String getLogRecString(Message msg) {
1027         final String logLine = String.format(
1028                 "%s/%d %d %d %s [%s]",
1029                 mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
1030                 msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
1031 
1032         final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
1033         mLog.log(richerLogLine);
1034         if (DBG) {
1035             Log.d(mTag, richerLogLine);
1036         }
1037 
1038         mMsgStateLogger.reset();
1039         return logLine;
1040     }
1041 
1042     @Override
recordLogRec(Message msg)1043     protected boolean recordLogRec(Message msg) {
1044         // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
1045         // and we already log any LinkProperties change that results in an
1046         // invocation of IpClient.Callback#onLinkPropertiesChange().
1047         final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
1048         if (!shouldLog) {
1049             mMsgStateLogger.reset();
1050         }
1051         return shouldLog;
1052     }
1053 
logError(String fmt, Throwable e, Object... args)1054     private void logError(String fmt, Throwable e, Object... args) {
1055         mLog.e(String.format(fmt, args), e);
1056     }
1057 
logError(String fmt, Object... args)1058     private void logError(String fmt, Object... args) {
1059         logError(fmt, null, args);
1060     }
1061 
1062     // This needs to be called with care to ensure that our LinkProperties
1063     // are in sync with the actual LinkProperties of the interface. For example,
1064     // we should only call this if we know for sure that there are no IP addresses
1065     // assigned to the interface, etc.
resetLinkProperties()1066     private void resetLinkProperties() {
1067         mLinkObserver.clearLinkProperties();
1068         mConfiguration = null;
1069         mDhcpResults = null;
1070         mTcpBufferSizes = "";
1071         mHttpProxy = null;
1072 
1073         mLinkProperties = new LinkProperties();
1074         mLinkProperties.setInterfaceName(mInterfaceName);
1075     }
1076 
recordMetric(final int type)1077     private void recordMetric(final int type) {
1078         // We may record error metrics prior to starting.
1079         // Map this to IMMEDIATE_FAILURE_DURATION.
1080         final long duration = (mStartTimeMillis > 0)
1081                 ? (SystemClock.elapsedRealtime() - mStartTimeMillis)
1082                 : IMMEDIATE_FAILURE_DURATION;
1083         mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
1084     }
1085 
1086     // Record the DisconnectCode and transition to StoppingState.
transitionToStoppingState(final DisconnectCode code)1087     private void transitionToStoppingState(final DisconnectCode code) {
1088         mIpProvisioningMetrics.setDisconnectCode(code);
1089         transitionTo(mStoppingState);
1090     }
1091 
1092     // For now: use WifiStateMachine's historical notion of provisioned.
1093     @VisibleForTesting
isProvisioned(LinkProperties lp, InitialConfiguration config)1094     static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
1095         // For historical reasons, we should connect even if all we have is
1096         // an IPv4 address and nothing else.
1097         if (lp.hasIpv4Address() || lp.isProvisioned()) {
1098             return true;
1099         }
1100         if (config == null) {
1101             return false;
1102         }
1103 
1104         // When an InitialConfiguration is specified, ignore any difference with previous
1105         // properties and instead check if properties observed match the desired properties.
1106         return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
1107     }
1108 
1109     // TODO: Investigate folding all this into the existing static function
1110     // LinkProperties.compareProvisioning() or some other single function that
1111     // takes two LinkProperties objects and returns a ProvisioningChange
1112     // object that is a correct and complete assessment of what changed, taking
1113     // account of the asymmetries described in the comments in this function.
1114     // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
compareProvisioning(LinkProperties oldLp, LinkProperties newLp)1115     private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
1116         int delta;
1117         InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
1118         final boolean wasProvisioned = isProvisioned(oldLp, config);
1119         final boolean isProvisioned = isProvisioned(newLp, config);
1120 
1121         if (!wasProvisioned && isProvisioned) {
1122             delta = PROV_CHANGE_GAINED_PROVISIONING;
1123         } else if (wasProvisioned && isProvisioned) {
1124             delta = PROV_CHANGE_STILL_PROVISIONED;
1125         } else if (!wasProvisioned && !isProvisioned) {
1126             delta = PROV_CHANGE_STILL_NOT_PROVISIONED;
1127         } else {
1128             // (wasProvisioned && !isProvisioned)
1129             //
1130             // Note that this is true even if we lose a configuration element
1131             // (e.g., a default gateway) that would not be required to advance
1132             // into provisioned state. This is intended: if we have a default
1133             // router and we lose it, that's a sure sign of a problem, but if
1134             // we connect to a network with no IPv4 DNS servers, we consider
1135             // that to be a network without DNS servers and connect anyway.
1136             //
1137             // See the comment below.
1138             delta = PROV_CHANGE_LOST_PROVISIONING;
1139         }
1140 
1141         final boolean lostIPv6 = oldLp.isIpv6Provisioned() && !newLp.isIpv6Provisioned();
1142         final boolean lostIPv4Address = oldLp.hasIpv4Address() && !newLp.hasIpv4Address();
1143         final boolean lostIPv6Router = oldLp.hasIpv6DefaultRoute() && !newLp.hasIpv6DefaultRoute();
1144 
1145         // If bad wifi avoidance is disabled, then ignore IPv6 loss of
1146         // provisioning. Otherwise, when a hotspot that loses Internet
1147         // access sends out a 0-lifetime RA to its clients, the clients
1148         // will disconnect and then reconnect, avoiding the bad hotspot,
1149         // instead of getting stuck on the bad hotspot. http://b/31827713 .
1150         //
1151         // This is incorrect because if the hotspot then regains Internet
1152         // access with a different prefix, TCP connections on the
1153         // deprecated addresses will remain stuck.
1154         //
1155         // Note that we can still be disconnected by IpReachabilityMonitor
1156         // if the IPv6 default gateway (but not the IPv6 DNS servers; see
1157         // accompanying code in IpReachabilityMonitor) is unreachable.
1158         final boolean ignoreIPv6ProvisioningLoss = mHasDisabledIPv6OnProvLoss
1159                 || (mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
1160                         && !mCm.shouldAvoidBadWifi());
1161 
1162         // Additionally:
1163         //
1164         // Partial configurations (e.g., only an IPv4 address with no DNS
1165         // servers and no default route) are accepted as long as DHCPv4
1166         // succeeds. On such a network, isProvisioned() will always return
1167         // false, because the configuration is not complete, but we want to
1168         // connect anyway. It might be a disconnected network such as a
1169         // Chromecast or a wireless printer, for example.
1170         //
1171         // Because on such a network isProvisioned() will always return false,
1172         // delta will never be LOST_PROVISIONING. So check for loss of
1173         // provisioning here too.
1174         if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
1175             delta = PROV_CHANGE_LOST_PROVISIONING;
1176         }
1177 
1178         // Additionally:
1179         //
1180         // If the previous link properties had a global IPv6 address and an
1181         // IPv6 default route then also consider the loss of that default route
1182         // to be a loss of provisioning. See b/27962810.
1183         if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
1184             // Although link properties have lost IPv6 default route in this case, if IPv4 is still
1185             // working with appropriate routes and DNS servers, we can keep the current connection
1186             // without disconnecting from the network, just disable IPv6 on that given network until
1187             // to the next provisioning. Disabling IPv6 will result in all IPv6 connectivity torn
1188             // down and all IPv6 sockets being closed, the non-routable IPv6 DNS servers will be
1189             // stripped out, so applications will be able to reconnect immediately over IPv4. See
1190             // b/131781810.
1191             if (newLp.isIpv4Provisioned()) {
1192                 mInterfaceCtrl.disableIPv6();
1193                 mHasDisabledIPv6OnProvLoss = true;
1194                 delta = PROV_CHANGE_STILL_PROVISIONED;
1195                 if (DBG) {
1196                     mLog.log("Disable IPv6 stack completely when the default router has gone");
1197                 }
1198             } else {
1199                 delta = PROV_CHANGE_LOST_PROVISIONING;
1200             }
1201         }
1202 
1203         return delta;
1204     }
1205 
dispatchCallback(int delta, LinkProperties newLp)1206     private void dispatchCallback(int delta, LinkProperties newLp) {
1207         switch (delta) {
1208             case PROV_CHANGE_GAINED_PROVISIONING:
1209                 if (DBG) {
1210                     Log.d(mTag, "onProvisioningSuccess()");
1211                 }
1212                 recordMetric(IpManagerEvent.PROVISIONING_OK);
1213                 mCallback.onProvisioningSuccess(newLp);
1214                 break;
1215 
1216             case PROV_CHANGE_LOST_PROVISIONING:
1217                 if (DBG) {
1218                     Log.d(mTag, "onProvisioningFailure()");
1219                 }
1220                 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
1221                 mCallback.onProvisioningFailure(newLp);
1222                 break;
1223 
1224             default:
1225                 if (DBG) {
1226                     Log.d(mTag, "onLinkPropertiesChange()");
1227                 }
1228                 mCallback.onLinkPropertiesChange(newLp);
1229                 break;
1230         }
1231     }
1232 
1233     // Updates all IpClient-related state concerned with LinkProperties.
1234     // Returns a ProvisioningChange for possibly notifying other interested
1235     // parties that are not fronted by IpClient.
setLinkProperties(LinkProperties newLp)1236     private int setLinkProperties(LinkProperties newLp) {
1237         if (mApfFilter != null) {
1238             mApfFilter.setLinkProperties(newLp);
1239         }
1240         if (mIpReachabilityMonitor != null) {
1241             mIpReachabilityMonitor.updateLinkProperties(newLp);
1242         }
1243 
1244         int delta = compareProvisioning(mLinkProperties, newLp);
1245         mLinkProperties = new LinkProperties(newLp);
1246 
1247         if (delta == PROV_CHANGE_GAINED_PROVISIONING) {
1248             // TODO: Add a proper ProvisionedState and cancel the alarm in
1249             // its enter() method.
1250             mProvisioningTimeoutAlarm.cancel();
1251         }
1252 
1253         return delta;
1254     }
1255 
assembleLinkProperties()1256     private LinkProperties assembleLinkProperties() {
1257         // [1] Create a new LinkProperties object to populate.
1258         LinkProperties newLp = new LinkProperties();
1259         newLp.setInterfaceName(mInterfaceName);
1260 
1261         // [2] Pull in data from netlink:
1262         //         - IPv4 addresses
1263         //         - IPv6 addresses
1264         //         - IPv6 routes
1265         //         - IPv6 DNS servers
1266         //
1267         // N.B.: this is fundamentally race-prone and should be fixed by
1268         // changing IpClientLinkObserver from a hybrid edge/level model to an
1269         // edge-only model, or by giving IpClient its own netlink socket(s)
1270         // so as to track all required information directly.
1271         LinkProperties netlinkLinkProperties = mLinkObserver.getLinkProperties();
1272         newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
1273         for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
1274             newLp.addRoute(route);
1275         }
1276         addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
1277         newLp.setNat64Prefix(netlinkLinkProperties.getNat64Prefix());
1278 
1279         // [3] Add in data from DHCPv4, if available.
1280         //
1281         // mDhcpResults is never shared with any other owner so we don't have
1282         // to worry about concurrent modification.
1283         if (mDhcpResults != null) {
1284             final List<RouteInfo> routes =
1285                     mDhcpResults.toStaticIpConfiguration().getRoutes(mInterfaceName);
1286             for (RouteInfo route : routes) {
1287                 newLp.addRoute(route);
1288             }
1289             addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
1290             newLp.setDomains(mDhcpResults.domains);
1291 
1292             if (mDhcpResults.mtu != 0) {
1293                 newLp.setMtu(mDhcpResults.mtu);
1294             }
1295 
1296             if (mDhcpResults.serverAddress != null) {
1297                 mShim.setDhcpServerAddress(newLp, mDhcpResults.serverAddress);
1298             }
1299 
1300             final String capportUrl = mDhcpResults.captivePortalApiUrl;
1301             // Uri.parse does no syntax check; do a simple check to eliminate garbage.
1302             // If the URL is still incorrect data fetching will fail later, which is fine.
1303             if (isParseableUrl(capportUrl)) {
1304                 NetworkInformationShimImpl.newInstance()
1305                         .setCaptivePortalApiUrl(newLp, Uri.parse(capportUrl));
1306             }
1307             // TODO: also look at the IPv6 RA (netlink) for captive portal URL
1308         }
1309 
1310         // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
1311         if (!TextUtils.isEmpty(mTcpBufferSizes)) {
1312             newLp.setTcpBufferSizes(mTcpBufferSizes);
1313         }
1314         if (mHttpProxy != null) {
1315             newLp.setHttpProxy(mHttpProxy);
1316         }
1317 
1318         // [5] Add data from InitialConfiguration
1319         if (mConfiguration != null && mConfiguration.mInitialConfig != null) {
1320             InitialConfiguration config = mConfiguration.mInitialConfig;
1321             // Add InitialConfiguration routes and dns server addresses once all addresses
1322             // specified in the InitialConfiguration have been observed with Netlink.
1323             if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) {
1324                 for (IpPrefix prefix : config.directlyConnectedRoutes) {
1325                     newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName, RTN_UNICAST));
1326                 }
1327             }
1328             addAllReachableDnsServers(newLp, config.dnsServers);
1329         }
1330         final LinkProperties oldLp = mLinkProperties;
1331         if (DBG) {
1332             Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
1333                     netlinkLinkProperties, newLp, oldLp));
1334         }
1335 
1336         // TODO: also learn via netlink routes specified by an InitialConfiguration and specified
1337         // from a static IP v4 config instead of manually patching them in in steps [3] and [5].
1338         return newLp;
1339     }
1340 
isParseableUrl(String url)1341     private static boolean isParseableUrl(String url) {
1342         // Verify that a URL has a reasonable format that can be parsed as per the URL constructor.
1343         // This does not use Patterns.WEB_URL as that pattern excludes URLs without TLDs, such as on
1344         // localhost.
1345         if (url == null) return false;
1346         try {
1347             new URL(url);
1348             return true;
1349         } catch (MalformedURLException e) {
1350             return false;
1351         }
1352     }
1353 
addAllReachableDnsServers( LinkProperties lp, Iterable<InetAddress> dnses)1354     private static void addAllReachableDnsServers(
1355             LinkProperties lp, Iterable<InetAddress> dnses) {
1356         // TODO: Investigate deleting this reachability check.  We should be
1357         // able to pass everything down to netd and let netd do evaluation
1358         // and RFC6724-style sorting.
1359         for (InetAddress dns : dnses) {
1360             if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
1361                 lp.addDnsServer(dns);
1362             }
1363         }
1364     }
1365 
1366     // Returns false if we have lost provisioning, true otherwise.
handleLinkPropertiesUpdate(boolean sendCallbacks)1367     private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
1368         final LinkProperties newLp = assembleLinkProperties();
1369         if (Objects.equals(newLp, mLinkProperties)) {
1370             return true;
1371         }
1372 
1373         // Either success IPv4 or IPv6 provisioning triggers new LinkProperties update,
1374         // wait for the provisioning completion and record the latency.
1375         mIpProvisioningMetrics.setIPv4ProvisionedLatencyOnFirstTime(newLp.isIpv4Provisioned());
1376         mIpProvisioningMetrics.setIPv6ProvisionedLatencyOnFirstTime(newLp.isIpv6Provisioned());
1377 
1378         final int delta = setLinkProperties(newLp);
1379         // Most of the attributes stored in the memory store are deduced from
1380         // the link properties, therefore when the properties update the memory
1381         // store record should be updated too.
1382         maybeSaveNetworkToIpMemoryStore();
1383         if (sendCallbacks) {
1384             dispatchCallback(delta, newLp);
1385         }
1386         return (delta != PROV_CHANGE_LOST_PROVISIONING);
1387     }
1388 
1389     @VisibleForTesting
removeDoubleQuotes(@onNull String ssid)1390     static String removeDoubleQuotes(@NonNull String ssid) {
1391         final int length = ssid.length();
1392         if ((length > 1) && (ssid.charAt(0) == '"') && (ssid.charAt(length - 1) == '"')) {
1393             return ssid.substring(1, length - 1);
1394         }
1395         return ssid;
1396     }
1397 
getVendorSpecificIEs(@onNull ScanResultInfo scanResultInfo)1398     private List<ByteBuffer> getVendorSpecificIEs(@NonNull ScanResultInfo scanResultInfo) {
1399         ArrayList<ByteBuffer> vendorSpecificPayloadList = new ArrayList<>();
1400         for (InformationElement ie : scanResultInfo.getInformationElements()) {
1401             if (ie.getId() == VENDOR_SPECIFIC_IE_ID) {
1402                 vendorSpecificPayloadList.add(ie.getPayload());
1403             }
1404         }
1405         return vendorSpecificPayloadList;
1406     }
1407 
detectUpstreamHotspotFromVendorIe()1408     private boolean detectUpstreamHotspotFromVendorIe() {
1409         if (mConfiguration.mScanResultInfo == null) return false;
1410         final ScanResultInfo scanResultInfo = mConfiguration.mScanResultInfo;
1411         final String ssid = scanResultInfo.getSsid();
1412         final List<ByteBuffer> vendorSpecificPayloadList = getVendorSpecificIEs(scanResultInfo);
1413 
1414         if (mConfiguration.mDisplayName == null
1415                 || !removeDoubleQuotes(mConfiguration.mDisplayName).equals(ssid)) {
1416             return false;
1417         }
1418 
1419         for (ByteBuffer payload : vendorSpecificPayloadList) {
1420             byte[] ouiAndType = new byte[4];
1421             try {
1422                 payload.get(ouiAndType);
1423             } catch (BufferUnderflowException e) {
1424                 Log.e(mTag, "Couldn't parse vendor specific IE, buffer underflow");
1425                 return false;
1426             }
1427             for (byte[] pattern : METERED_IE_PATTERN_LIST) {
1428                 if (Arrays.equals(pattern, ouiAndType)) {
1429                     if (DBG) {
1430                         Log.d(mTag, "detected upstream hotspot that matches OUI:"
1431                                 + HexDump.toHexString(ouiAndType));
1432                     }
1433                     return true;
1434                 }
1435             }
1436         }
1437         return false;
1438     }
1439 
handleIPv4Success(DhcpResults dhcpResults)1440     private void handleIPv4Success(DhcpResults dhcpResults) {
1441         mDhcpResults = new DhcpResults(dhcpResults);
1442         final LinkProperties newLp = assembleLinkProperties();
1443         final int delta = setLinkProperties(newLp);
1444 
1445         if (mDhcpResults.vendorInfo == null && detectUpstreamHotspotFromVendorIe()) {
1446             mDhcpResults.vendorInfo = DhcpPacket.VENDOR_INFO_ANDROID_METERED;
1447         }
1448 
1449         if (DBG) {
1450             Log.d(mTag, "onNewDhcpResults(" + Objects.toString(mDhcpResults) + ")");
1451             Log.d(mTag, "handleIPv4Success newLp{" + newLp + "}");
1452         }
1453         mCallback.onNewDhcpResults(mDhcpResults);
1454         maybeSaveNetworkToIpMemoryStore();
1455 
1456         dispatchCallback(delta, newLp);
1457     }
1458 
handleIPv4Failure()1459     private void handleIPv4Failure() {
1460         // TODO: Investigate deleting this clearIPv4Address() call.
1461         //
1462         // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
1463         // that could trigger a call to this function. If we missed handling
1464         // that message in StartedState for some reason we would still clear
1465         // any addresses upon entry to StoppedState.
1466         mInterfaceCtrl.clearIPv4Address();
1467         mDhcpResults = null;
1468         if (DBG) {
1469             Log.d(mTag, "onNewDhcpResults(null)");
1470         }
1471         mCallback.onNewDhcpResults(null);
1472 
1473         handleProvisioningFailure(DisconnectCode.DC_PROVISIONING_FAIL);
1474     }
1475 
handleProvisioningFailure(final DisconnectCode code)1476     private void handleProvisioningFailure(final DisconnectCode code) {
1477         final LinkProperties newLp = assembleLinkProperties();
1478         int delta = setLinkProperties(newLp);
1479         // If we've gotten here and we're still not provisioned treat that as
1480         // a total loss of provisioning.
1481         //
1482         // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
1483         // there was no usable IPv6 obtained before a non-zero provisioning
1484         // timeout expired.
1485         //
1486         // Regardless: GAME OVER.
1487         if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) {
1488             delta = PROV_CHANGE_LOST_PROVISIONING;
1489         }
1490 
1491         dispatchCallback(delta, newLp);
1492         if (delta == PROV_CHANGE_LOST_PROVISIONING) {
1493             transitionToStoppingState(code);
1494         }
1495     }
1496 
doImmediateProvisioningFailure(int failureType)1497     private void doImmediateProvisioningFailure(int failureType) {
1498         logError("onProvisioningFailure(): %s", failureType);
1499         recordMetric(failureType);
1500         mCallback.onProvisioningFailure(mLinkProperties);
1501     }
1502 
startIPv4()1503     private boolean startIPv4() {
1504         // If we have a StaticIpConfiguration attempt to apply it and
1505         // handle the result accordingly.
1506         if (mConfiguration.mStaticIpConfig != null) {
1507             if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.getIpAddress())) {
1508                 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
1509             } else {
1510                 return false;
1511             }
1512         } else {
1513             if (mDhcpClient != null) {
1514                 Log.wtf(mTag, "DhcpClient should never be non-null in startIPv4()");
1515             }
1516             startDhcpClient();
1517         }
1518 
1519         return true;
1520     }
1521 
startIPv6()1522     private boolean startIPv6() {
1523         return mInterfaceCtrl.setIPv6PrivacyExtensions(true)
1524                 && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode)
1525                 && mInterfaceCtrl.enableIPv6();
1526     }
1527 
applyInitialConfig(InitialConfiguration config)1528     private boolean applyInitialConfig(InitialConfiguration config) {
1529         // TODO: also support specifying a static IPv4 configuration in InitialConfiguration.
1530         for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIpv6)) {
1531             if (!mInterfaceCtrl.addAddress(addr)) return false;
1532         }
1533 
1534         return true;
1535     }
1536 
startIpReachabilityMonitor()1537     private boolean startIpReachabilityMonitor() {
1538         try {
1539             mIpReachabilityMonitor = new IpReachabilityMonitor(
1540                     mContext,
1541                     mInterfaceParams,
1542                     getHandler(),
1543                     mLog,
1544                     new IpReachabilityMonitor.Callback() {
1545                         @Override
1546                         public void notifyLost(InetAddress ip, String logMsg) {
1547                             mCallback.onReachabilityLost(logMsg);
1548                         }
1549                     },
1550                     mConfiguration.mUsingMultinetworkPolicyTracker,
1551                     mNetd);
1552         } catch (IllegalArgumentException iae) {
1553             // Failed to start IpReachabilityMonitor. Log it and call
1554             // onProvisioningFailure() immediately.
1555             //
1556             // See http://b/31038971.
1557             logError("IpReachabilityMonitor failure: %s", iae);
1558             mIpReachabilityMonitor = null;
1559         }
1560 
1561         return (mIpReachabilityMonitor != null);
1562     }
1563 
stopAllIP()1564     private void stopAllIP() {
1565         // We don't need to worry about routes, just addresses, because:
1566         //     - disableIpv6() will clear autoconf IPv6 routes as well, and
1567         //     - we don't get IPv4 routes from netlink
1568         // so we neither react to nor need to wait for changes in either.
1569 
1570         mInterfaceCtrl.disableIPv6();
1571         mInterfaceCtrl.clearAllAddresses();
1572     }
1573 
maybeSaveNetworkToIpMemoryStore()1574     private void maybeSaveNetworkToIpMemoryStore() {
1575         // TODO : implement this
1576     }
1577 
maybeRestoreInterfaceMtu()1578     private void maybeRestoreInterfaceMtu() {
1579         InterfaceParams params = mDependencies.getInterfaceParams(mInterfaceName);
1580         if (params == null) {
1581             Log.w(mTag, "interface: " + mInterfaceName + " is gone");
1582             return;
1583         }
1584 
1585         if (params.index != mInterfaceParams.index) {
1586             Log.w(mTag, "interface: " + mInterfaceName + " has a different index: " + params.index);
1587             return;
1588         }
1589 
1590         if (params.defaultMtu == mInterfaceParams.defaultMtu) return;
1591 
1592         try {
1593             mNetd.interfaceSetMtu(mInterfaceName, mInterfaceParams.defaultMtu);
1594         } catch (RemoteException | ServiceSpecificException e) {
1595             logError("Couldn't reset MTU on " + mInterfaceName + " from "
1596                     + params.defaultMtu + " to " + mInterfaceParams.defaultMtu, e);
1597         }
1598     }
1599 
handleUpdateL2Information(@onNull Layer2InformationParcelable info)1600     private void handleUpdateL2Information(@NonNull Layer2InformationParcelable info) {
1601         mL2Key = info.l2Key;
1602         mCluster = info.cluster;
1603 
1604         if (info.bssid == null || mCurrentBssid == null) {
1605             Log.wtf(mTag, "bssid in the parcelable or current tracked bssid should be non-null");
1606             return;
1607         }
1608 
1609         // If the BSSID has not changed, there is nothing to do.
1610         if (info.bssid.equals(mCurrentBssid)) return;
1611 
1612         if (mIpReachabilityMonitor != null) {
1613             mIpReachabilityMonitor.probeAll();
1614         }
1615 
1616         // Check whether to refresh previous IP lease on L2 roaming happened.
1617         final String ssid = removeDoubleQuotes(mConfiguration.mDisplayName);
1618         if (DHCP_ROAMING_SSID_SET.contains(ssid) && mDhcpClient != null) {
1619             if (DBG) {
1620                 Log.d(mTag, "L2 roaming happened from " + mCurrentBssid
1621                         + " to " + info.bssid
1622                         + " , SSID: " + ssid
1623                         + " , starting refresh leased IP address");
1624             }
1625             mDhcpClient.sendMessage(DhcpClient.CMD_REFRESH_LINKADDRESS);
1626         }
1627         mCurrentBssid = info.bssid;
1628     }
1629 
1630     class StoppedState extends State {
1631         @Override
enter()1632         public void enter() {
1633             stopAllIP();
1634             mHasDisabledIPv6OnProvLoss = false;
1635 
1636             mLinkObserver.clearInterfaceParams();
1637             resetLinkProperties();
1638             if (mStartTimeMillis > 0) {
1639                 // Completed a life-cycle; send a final empty LinkProperties
1640                 // (cleared in resetLinkProperties() above) and record an event.
1641                 mCallback.onLinkPropertiesChange(mLinkProperties);
1642                 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
1643                 mStartTimeMillis = 0;
1644             }
1645         }
1646 
1647         @Override
processMessage(Message msg)1648         public boolean processMessage(Message msg) {
1649             switch (msg.what) {
1650                 case CMD_TERMINATE_AFTER_STOP:
1651                     stopStateMachineUpdaters();
1652                     quit();
1653                     break;
1654 
1655                 case CMD_STOP:
1656                     break;
1657 
1658                 case CMD_START:
1659                     mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
1660                     transitionTo(mClearingIpAddressesState);
1661                     break;
1662 
1663                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
1664                     handleLinkPropertiesUpdate(NO_CALLBACKS);
1665                     break;
1666 
1667                 case CMD_UPDATE_TCP_BUFFER_SIZES:
1668                     mTcpBufferSizes = (String) msg.obj;
1669                     handleLinkPropertiesUpdate(NO_CALLBACKS);
1670                     break;
1671 
1672                 case CMD_UPDATE_HTTP_PROXY:
1673                     mHttpProxy = (ProxyInfo) msg.obj;
1674                     handleLinkPropertiesUpdate(NO_CALLBACKS);
1675                     break;
1676 
1677                 case CMD_UPDATE_L2KEY_CLUSTER: {
1678                     final Pair<String, String> args = (Pair<String, String>) msg.obj;
1679                     mL2Key = args.first;
1680                     mCluster = args.second;
1681                     break;
1682                 }
1683 
1684                 case CMD_SET_MULTICAST_FILTER:
1685                     mMulticastFiltering = (boolean) msg.obj;
1686                     break;
1687 
1688                 case DhcpClient.CMD_ON_QUIT:
1689                     // Everything is already stopped.
1690                     logError("Unexpected CMD_ON_QUIT (already stopped).");
1691                     break;
1692 
1693                 default:
1694                     return NOT_HANDLED;
1695             }
1696 
1697             mMsgStateLogger.handled(this, getCurrentState());
1698             return HANDLED;
1699         }
1700     }
1701 
1702     class StoppingState extends State {
1703         @Override
enter()1704         public void enter() {
1705             if (mDhcpClient == null) {
1706                 // There's no DHCPv4 for which to wait; proceed to stopped.
1707                 deferMessage(obtainMessage(CMD_JUMP_STOPPING_TO_STOPPED));
1708             }
1709 
1710             // Restore the interface MTU to initial value if it has changed.
1711             maybeRestoreInterfaceMtu();
1712         }
1713 
1714         @Override
processMessage(Message msg)1715         public boolean processMessage(Message msg) {
1716             switch (msg.what) {
1717                 case CMD_JUMP_STOPPING_TO_STOPPED:
1718                     transitionTo(mStoppedState);
1719                     break;
1720 
1721                 case CMD_STOP:
1722                     break;
1723 
1724                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
1725                     mInterfaceCtrl.clearIPv4Address();
1726                     break;
1727 
1728                 case DhcpClient.CMD_ON_QUIT:
1729                     mDhcpClient = null;
1730                     transitionTo(mStoppedState);
1731                     break;
1732 
1733                 default:
1734                     deferMessage(msg);
1735             }
1736 
1737             mMsgStateLogger.handled(this, getCurrentState());
1738             return HANDLED;
1739         }
1740     }
1741 
isUsingPreconnection()1742     private boolean isUsingPreconnection() {
1743         return mConfiguration.mEnablePreconnection && mConfiguration.mStaticIpConfig == null;
1744     }
1745 
startDhcpClient()1746     private void startDhcpClient() {
1747         // Start DHCPv4.
1748         mDhcpClient = mDependencies.makeDhcpClient(mContext, IpClient.this, mInterfaceParams,
1749                 mDependencies.getDhcpClientDependencies(mIpMemoryStore, mIpProvisioningMetrics));
1750 
1751         // If preconnection is enabled, there is no need to ask Wi-Fi to disable powersaving
1752         // during DHCP, because the DHCP handshake will happen during association. In order to
1753         // ensure that future renews still do the DHCP action (if configured),
1754         // registerForPreDhcpNotification is called later when processing the CMD_*_PRECONNECTION
1755         // messages.
1756         if (!isUsingPreconnection()) mDhcpClient.registerForPreDhcpNotification();
1757         mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP, new DhcpClient.Configuration(mL2Key,
1758                 isUsingPreconnection()));
1759     }
1760 
1761     class ClearingIpAddressesState extends State {
1762         @Override
enter()1763         public void enter() {
1764             // Ensure that interface parameters are fetched on the handler thread so they are
1765             // properly ordered with other events, such as restoring the interface MTU on teardown.
1766             mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
1767             if (mInterfaceParams == null) {
1768                 logError("Failed to find InterfaceParams for " + mInterfaceName);
1769                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
1770                 deferMessage(obtainMessage(CMD_STOP,
1771                         DisconnectCode.DC_INTERFACE_NOT_FOUND.getNumber()));
1772                 return;
1773             }
1774 
1775             mLinkObserver.setInterfaceParams(mInterfaceParams);
1776 
1777             if (readyToProceed()) {
1778                 deferMessage(obtainMessage(CMD_ADDRESSES_CLEARED));
1779             } else {
1780                 // Clear all IPv4 and IPv6 before proceeding to RunningState.
1781                 // Clean up any leftover state from an abnormal exit from
1782                 // tethering or during an IpClient restart.
1783                 stopAllIP();
1784             }
1785 
1786             mCallback.setNeighborDiscoveryOffload(true);
1787         }
1788 
1789         @Override
processMessage(Message msg)1790         public boolean processMessage(Message msg) {
1791             switch (msg.what) {
1792                 case CMD_ADDRESSES_CLEARED:
1793                     transitionTo(isUsingPreconnection() ? mPreconnectingState : mRunningState);
1794                     break;
1795 
1796                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
1797                     handleLinkPropertiesUpdate(NO_CALLBACKS);
1798                     if (readyToProceed()) {
1799                         transitionTo(isUsingPreconnection() ? mPreconnectingState : mRunningState);
1800                     }
1801                     break;
1802 
1803                 case CMD_STOP:
1804                 case EVENT_PROVISIONING_TIMEOUT:
1805                     // Fall through to StartedState.
1806                     return NOT_HANDLED;
1807 
1808                 default:
1809                     // It's safe to process messages out of order because the
1810                     // only message that can both
1811                     //     a) be received at this time and
1812                     //     b) affect provisioning state
1813                     // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
1814                     deferMessage(msg);
1815             }
1816             return HANDLED;
1817         }
1818 
readyToProceed()1819         private boolean readyToProceed() {
1820             return !mLinkProperties.hasIpv4Address() && !mLinkProperties.hasGlobalIpv6Address();
1821         }
1822     }
1823 
1824     class PreconnectingState extends State {
1825         @Override
enter()1826         public void enter() {
1827             startDhcpClient();
1828         }
1829 
1830         @Override
processMessage(Message msg)1831         public boolean processMessage(Message msg) {
1832             switch (msg.what) {
1833                 case CMD_COMPLETE_PRECONNECTION:
1834                     boolean success = (msg.arg1 == 1);
1835                     mDhcpClient.registerForPreDhcpNotification();
1836                     if (!success) {
1837                         mDhcpClient.sendMessage(DhcpClient.CMD_ABORT_PRECONNECTION);
1838                     }
1839                     // The link is ready for use. Advance to running state, start IPv6, etc.
1840                     transitionTo(mRunningState);
1841                     break;
1842 
1843                 case DhcpClient.CMD_START_PRECONNECTION:
1844                     final Layer2PacketParcelable l2Packet = (Layer2PacketParcelable) msg.obj;
1845                     mCallback.onPreconnectionStart(Collections.singletonList(l2Packet));
1846                     break;
1847 
1848                 case CMD_STOP:
1849                 case EVENT_PROVISIONING_TIMEOUT:
1850                     // Fall through to StartedState.
1851                     return NOT_HANDLED;
1852 
1853                 default:
1854                     deferMessage(msg);
1855             }
1856             return HANDLED;
1857         }
1858     }
1859 
1860     class StartedState extends State {
1861         @Override
enter()1862         public void enter() {
1863             mIpProvisioningMetrics.reset();
1864             mStartTimeMillis = SystemClock.elapsedRealtime();
1865             if (mConfiguration.mProvisioningTimeoutMs > 0) {
1866                 final long alarmTime = SystemClock.elapsedRealtime()
1867                         + mConfiguration.mProvisioningTimeoutMs;
1868                 mProvisioningTimeoutAlarm.schedule(alarmTime);
1869             }
1870         }
1871 
1872         @Override
exit()1873         public void exit() {
1874             mProvisioningTimeoutAlarm.cancel();
1875 
1876             // Record metrics information once this provisioning has completed due to certain
1877             // reason (normal termination, provisioning timeout, lost provisioning and etc).
1878             mIpProvisioningMetrics.statsWrite();
1879         }
1880 
1881         @Override
processMessage(Message msg)1882         public boolean processMessage(Message msg) {
1883             switch (msg.what) {
1884                 case CMD_STOP:
1885                     transitionToStoppingState(DisconnectCode.forNumber(msg.arg1));
1886                     break;
1887 
1888                 case CMD_UPDATE_L2KEY_CLUSTER: {
1889                     final Pair<String, String> args = (Pair<String, String>) msg.obj;
1890                     mL2Key = args.first;
1891                     mCluster = args.second;
1892                     // TODO : attributes should be saved to the memory store with
1893                     // these new values if they differ from the previous ones.
1894                     // If the state machine is in pure StartedState, then the values to input
1895                     // are not known yet and should be updated when the LinkProperties are updated.
1896                     // If the state machine is in RunningState (which is a child of StartedState)
1897                     // then the next NUD check should be used to store the new values to avoid
1898                     // inputting current values for what may be a different L3 network.
1899                     break;
1900                 }
1901 
1902                 case CMD_UPDATE_L2INFORMATION:
1903                     handleUpdateL2Information((Layer2InformationParcelable) msg.obj);
1904                     break;
1905 
1906                 case EVENT_PROVISIONING_TIMEOUT:
1907                     handleProvisioningFailure(DisconnectCode.DC_PROVISIONING_TIMEOUT);
1908                     break;
1909 
1910                 default:
1911                     return NOT_HANDLED;
1912             }
1913 
1914             mMsgStateLogger.handled(this, getCurrentState());
1915             return HANDLED;
1916         }
1917     }
1918 
1919     class RunningState extends State {
1920         private ConnectivityPacketTracker mPacketTracker;
1921         private boolean mDhcpActionInFlight;
1922 
1923         @Override
enter()1924         public void enter() {
1925             ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
1926             apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
1927             apfConfig.multicastFilter = mMulticastFiltering;
1928             // Get the Configuration for ApfFilter from Context
1929             apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames();
1930             apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList();
1931             apfConfig.minRdnssLifetimeSec = mMinRdnssLifetimeSec;
1932             mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
1933             // TODO: investigate the effects of any multicast filtering racing/interfering with the
1934             // rest of this IP configuration startup.
1935             if (mApfFilter == null) {
1936                 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
1937             }
1938 
1939             mPacketTracker = createPacketTracker();
1940             if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
1941 
1942             if (mConfiguration.mEnableIPv6 && !startIPv6()) {
1943                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
1944                 enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV6);
1945                 return;
1946             }
1947 
1948             if (mConfiguration.mEnableIPv4 && !isUsingPreconnection() && !startIPv4()) {
1949                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
1950                 enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV4);
1951                 return;
1952             }
1953 
1954             final InitialConfiguration config = mConfiguration.mInitialConfig;
1955             if ((config != null) && !applyInitialConfig(config)) {
1956                 // TODO introduce a new IpManagerEvent constant to distinguish this error case.
1957                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
1958                 enqueueJumpToStoppingState(DisconnectCode.DC_INVALID_PROVISIONING);
1959                 return;
1960             }
1961 
1962             if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
1963                 doImmediateProvisioningFailure(
1964                         IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
1965                 enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPREACHABILITYMONITOR);
1966                 return;
1967             }
1968         }
1969 
1970         @Override
exit()1971         public void exit() {
1972             stopDhcpAction();
1973 
1974             if (mIpReachabilityMonitor != null) {
1975                 mIpReachabilityMonitor.stop();
1976                 mIpReachabilityMonitor = null;
1977             }
1978 
1979             if (mDhcpClient != null) {
1980                 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
1981                 mDhcpClient.doQuit();
1982             }
1983 
1984             if (mPacketTracker != null) {
1985                 mPacketTracker.stop();
1986                 mPacketTracker = null;
1987             }
1988 
1989             if (mApfFilter != null) {
1990                 mApfFilter.shutdown();
1991                 mApfFilter = null;
1992             }
1993 
1994             resetLinkProperties();
1995         }
1996 
enqueueJumpToStoppingState(final DisconnectCode code)1997         private void enqueueJumpToStoppingState(final DisconnectCode code) {
1998             deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING, code.getNumber()));
1999         }
2000 
createPacketTracker()2001         private ConnectivityPacketTracker createPacketTracker() {
2002             try {
2003                 return new ConnectivityPacketTracker(
2004                         getHandler(), mInterfaceParams, mConnectivityPacketLog);
2005             } catch (IllegalArgumentException e) {
2006                 return null;
2007             }
2008         }
2009 
ensureDhcpAction()2010         private void ensureDhcpAction() {
2011             if (!mDhcpActionInFlight) {
2012                 mCallback.onPreDhcpAction();
2013                 mDhcpActionInFlight = true;
2014                 final long alarmTime = SystemClock.elapsedRealtime()
2015                         + mConfiguration.mRequestedPreDhcpActionMs;
2016                 mDhcpActionTimeoutAlarm.schedule(alarmTime);
2017             }
2018         }
2019 
stopDhcpAction()2020         private void stopDhcpAction() {
2021             mDhcpActionTimeoutAlarm.cancel();
2022             if (mDhcpActionInFlight) {
2023                 mCallback.onPostDhcpAction();
2024                 mDhcpActionInFlight = false;
2025             }
2026         }
2027 
2028         @Override
processMessage(Message msg)2029         public boolean processMessage(Message msg) {
2030             switch (msg.what) {
2031                 case CMD_JUMP_RUNNING_TO_STOPPING:
2032                 case CMD_STOP:
2033                     transitionToStoppingState(DisconnectCode.forNumber(msg.arg1));
2034                     break;
2035 
2036                 case CMD_START:
2037                     logError("ALERT: START received in StartedState. Please fix caller.");
2038                     break;
2039 
2040                 case CMD_CONFIRM:
2041                     // TODO: Possibly introduce a second type of confirmation
2042                     // that both probes (a) on-link neighbors and (b) does
2043                     // a DHCPv4 RENEW.  We used to do this on Wi-Fi framework
2044                     // roams.
2045                     if (mIpReachabilityMonitor != null) {
2046                         mIpReachabilityMonitor.probeAll();
2047                     }
2048                     break;
2049 
2050                 case EVENT_PRE_DHCP_ACTION_COMPLETE:
2051                     // It's possible to reach here if, for example, someone
2052                     // calls completedPreDhcpAction() after provisioning with
2053                     // a static IP configuration.
2054                     if (mDhcpClient != null) {
2055                         mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
2056                     }
2057                     break;
2058 
2059                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
2060                     // EVENT_NETLINK_LINKPROPERTIES_CHANGED message will be received in both of
2061                     // provisioning loss and normal user termination cases (e.g. turn off wifi or
2062                     // switch to another wifi ssid), hence, checking the current interface link
2063                     // state (down or up) helps distinguish the two cases: if the link state is
2064                     // down, provisioning is only lost because the link is being torn down (for
2065                     // example when turning off wifi), so treat it as a normal termination.
2066                     if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
2067                         final boolean linkStateUp = (msg.arg1 == ARG_LINKPROP_CHANGED_LINKSTATE_UP);
2068                         transitionToStoppingState(linkStateUp ? DisconnectCode.DC_PROVISIONING_FAIL
2069                                 : DisconnectCode.DC_NORMAL_TERMINATION);
2070                     }
2071                     break;
2072 
2073                 case CMD_UPDATE_TCP_BUFFER_SIZES:
2074                     mTcpBufferSizes = (String) msg.obj;
2075                     // This cannot possibly change provisioning state.
2076                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
2077                     break;
2078 
2079                 case CMD_UPDATE_HTTP_PROXY:
2080                     mHttpProxy = (ProxyInfo) msg.obj;
2081                     // This cannot possibly change provisioning state.
2082                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
2083                     break;
2084 
2085                 case CMD_SET_MULTICAST_FILTER: {
2086                     mMulticastFiltering = (boolean) msg.obj;
2087                     if (mApfFilter != null) {
2088                         mApfFilter.setMulticastFilter(mMulticastFiltering);
2089                     } else {
2090                         mCallback.setFallbackMulticastFilter(mMulticastFiltering);
2091                     }
2092                     break;
2093                 }
2094 
2095                 case EVENT_READ_PACKET_FILTER_COMPLETE: {
2096                     if (mApfFilter != null) {
2097                         mApfFilter.setDataSnapshot((byte[]) msg.obj);
2098                     }
2099                     mApfDataSnapshotComplete.open();
2100                     break;
2101                 }
2102 
2103                 case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: {
2104                     final int slot = msg.arg1;
2105 
2106                     if (mApfFilter != null) {
2107                         if (msg.obj instanceof NattKeepalivePacketDataParcelable) {
2108                             mApfFilter.addNattKeepalivePacketFilter(slot,
2109                                     (NattKeepalivePacketDataParcelable) msg.obj);
2110                         } else if (msg.obj instanceof TcpKeepalivePacketDataParcelable) {
2111                             mApfFilter.addTcpKeepalivePacketFilter(slot,
2112                                     (TcpKeepalivePacketDataParcelable) msg.obj);
2113                         }
2114                     }
2115                     break;
2116                 }
2117 
2118                 case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: {
2119                     final int slot = msg.arg1;
2120                     if (mApfFilter != null) {
2121                         mApfFilter.removeKeepalivePacketFilter(slot);
2122                     }
2123                     break;
2124                 }
2125 
2126                 case EVENT_DHCPACTION_TIMEOUT:
2127                     stopDhcpAction();
2128                     break;
2129 
2130                 case DhcpClient.CMD_PRE_DHCP_ACTION:
2131                     if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
2132                         ensureDhcpAction();
2133                     } else {
2134                         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
2135                     }
2136                     break;
2137 
2138                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
2139                     mInterfaceCtrl.clearIPv4Address();
2140                     break;
2141 
2142                 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
2143                     final LinkAddress ipAddress = (LinkAddress) msg.obj;
2144                     if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
2145                         mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
2146                     } else {
2147                         logError("Failed to set IPv4 address.");
2148                         dispatchCallback(PROV_CHANGE_LOST_PROVISIONING, mLinkProperties);
2149                         transitionToStoppingState(DisconnectCode.DC_PROVISIONING_FAIL);
2150                     }
2151                     break;
2152                 }
2153 
2154                 // This message is only received when:
2155                 //
2156                 //     a) initial address acquisition succeeds,
2157                 //     b) renew succeeds or is NAK'd,
2158                 //     c) rebind succeeds or is NAK'd, or
2159                 //     c) the lease expires,
2160                 //
2161                 // but never when initial address acquisition fails. The latter
2162                 // condition is now governed by the provisioning timeout.
2163                 case DhcpClient.CMD_POST_DHCP_ACTION:
2164                     stopDhcpAction();
2165 
2166                     switch (msg.arg1) {
2167                         case DhcpClient.DHCP_SUCCESS:
2168                             handleIPv4Success((DhcpResults) msg.obj);
2169                             break;
2170                         case DhcpClient.DHCP_FAILURE:
2171                             handleIPv4Failure();
2172                             break;
2173                         default:
2174                             logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
2175                     }
2176                     break;
2177 
2178                 case DhcpClient.CMD_ON_QUIT:
2179                     // DHCPv4 quit early for some reason.
2180                     logError("Unexpected CMD_ON_QUIT.");
2181                     mDhcpClient = null;
2182                     break;
2183 
2184                 default:
2185                     return NOT_HANDLED;
2186             }
2187 
2188             mMsgStateLogger.handled(this, getCurrentState());
2189             return HANDLED;
2190         }
2191     }
2192 
2193     private static class MessageHandlingLogger {
2194         public String processedInState;
2195         public String receivedInState;
2196 
reset()2197         public void reset() {
2198             processedInState = null;
2199             receivedInState = null;
2200         }
2201 
handled(State processedIn, IState receivedIn)2202         public void handled(State processedIn, IState receivedIn) {
2203             processedInState = processedIn.getClass().getSimpleName();
2204             receivedInState = receivedIn.getName();
2205         }
2206 
toString()2207         public String toString() {
2208             return String.format("rcvd_in=%s, proc_in=%s",
2209                                  receivedInState, processedInState);
2210         }
2211     }
2212 
2213     // TODO: extract out into CollectionUtils.
any(Iterable<T> coll, Predicate<T> fn)2214     static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
2215         for (T t : coll) {
2216             if (fn.test(t)) {
2217                 return true;
2218             }
2219         }
2220         return false;
2221     }
2222 
all(Iterable<T> coll, Predicate<T> fn)2223     static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
2224         return !any(coll, not(fn));
2225     }
2226 
not(Predicate<T> fn)2227     static <T> Predicate<T> not(Predicate<T> fn) {
2228         return (t) -> !fn.test(t);
2229     }
2230 
join(String delimiter, Collection<T> coll)2231     static <T> String join(String delimiter, Collection<T> coll) {
2232         return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter));
2233     }
2234 
find(Iterable<T> coll, Predicate<T> fn)2235     static <T> T find(Iterable<T> coll, Predicate<T> fn) {
2236         for (T t: coll) {
2237             if (fn.test(t)) {
2238                 return t;
2239             }
2240         }
2241         return null;
2242     }
2243 
findAll(Collection<T> coll, Predicate<T> fn)2244     static <T> List<T> findAll(Collection<T> coll, Predicate<T> fn) {
2245         return coll.stream().filter(fn).collect(Collectors.toList());
2246     }
2247 }
2248