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