[go: nahoru, domu]

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