1/*
2 * Copyright (C) 2014 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 com.android.server.ethernet;
18
19import android.content.Context;
20import android.net.ConnectivityManager;
21import android.net.DhcpResults;
22import android.net.EthernetManager;
23import android.net.IEthernetServiceListener;
24import android.net.InterfaceConfiguration;
25import android.net.IpConfiguration;
26import android.net.IpConfiguration.IpAssignment;
27import android.net.IpConfiguration.ProxySettings;
28import android.net.LinkProperties;
29import android.net.NetworkAgent;
30import android.net.NetworkCapabilities;
31import android.net.NetworkFactory;
32import android.net.NetworkInfo;
33import android.net.NetworkInfo.DetailedState;
34import android.net.StaticIpConfiguration;
35import android.net.ip.IpManager;
36import android.net.ip.IpManager.ProvisioningConfiguration;
37import android.os.Handler;
38import android.os.IBinder;
39import android.os.INetworkManagementService;
40import android.os.Looper;
41import android.os.RemoteCallbackList;
42import android.os.RemoteException;
43import android.os.ServiceManager;
44import android.text.TextUtils;
45import android.util.Log;
46
47import com.android.internal.util.IndentingPrintWriter;
48import com.android.server.net.BaseNetworkObserver;
49
50import java.io.FileDescriptor;
51import java.io.PrintWriter;
52import java.util.Arrays;
53import java.util.concurrent.CountDownLatch;
54
55
56/**
57 * Manages connectivity for an Ethernet interface.
58 *
59 * Ethernet Interfaces may be present at boot time or appear after boot (e.g.,
60 * for Ethernet adapters connected over USB). This class currently supports
61 * only one interface. When an interface appears on the system (or is present
62 * at boot time) this class will start tracking it and bring it up, and will
63 * attempt to connect when requested. Any other interfaces that subsequently
64 * appear will be ignored until the tracked interface disappears. Only
65 * interfaces whose names match the <code>config_ethernet_iface_regex</code>
66 * regular expression are tracked.
67 *
68 * This class reports a static network score of 70 when it is tracking an
69 * interface and that interface's link is up, and a score of 0 otherwise.
70 *
71 * @hide
72 */
73class EthernetNetworkFactory {
74    private static final String NETWORK_TYPE = "Ethernet";
75    private static final String TAG = "EthernetNetworkFactory";
76    private static final int NETWORK_SCORE = 70;
77    private static final boolean DBG = true;
78
79    /** Tracks interface changes. Called from NetworkManagementService. */
80    private InterfaceObserver mInterfaceObserver;
81
82    /** For static IP configuration */
83    private EthernetManager mEthernetManager;
84
85    /** To set link state and configure IP addresses. */
86    private INetworkManagementService mNMService;
87
88    /** All code runs here, including start(). */
89    private Handler mHandler;
90
91    /* To communicate with ConnectivityManager */
92    private NetworkCapabilities mNetworkCapabilities;
93    private NetworkAgent mNetworkAgent;
94    private LocalNetworkFactory mFactory;
95    private Context mContext;
96
97    /** Product-dependent regular expression of interface names we track. */
98    private static String mIfaceMatch = "";
99
100    /** To notify Ethernet status. */
101    private final RemoteCallbackList<IEthernetServiceListener> mListeners;
102
103    /** Data members. All accesses to these must be on the handler thread. */
104    private String mIface = "";
105    private String mHwAddr;
106    private boolean mLinkUp;
107    private NetworkInfo mNetworkInfo;
108    private LinkProperties mLinkProperties;
109    private IpManager mIpManager;
110    private boolean mNetworkRequested = false;
111
112    EthernetNetworkFactory(RemoteCallbackList<IEthernetServiceListener> listeners) {
113        initNetworkCapabilities();
114        clearInfo();
115        mListeners = listeners;
116    }
117
118    private class LocalNetworkFactory extends NetworkFactory {
119        LocalNetworkFactory(String name, Context context, Looper looper) {
120            super(looper, context, name, new NetworkCapabilities());
121        }
122
123        protected void startNetwork() {
124            if (!mNetworkRequested) {
125                mNetworkRequested = true;
126                maybeStartIpManager();
127            }
128        }
129
130        protected void stopNetwork() {
131            mNetworkRequested = false;
132            stopIpManager();
133        }
134    }
135
136    private void clearInfo() {
137        mLinkProperties = new LinkProperties();
138        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");
139        mNetworkInfo.setExtraInfo(mHwAddr);
140        mNetworkInfo.setIsAvailable(isTrackingInterface());
141    }
142
143    private void stopIpManager() {
144        if (mIpManager != null) {
145            mIpManager.shutdown();
146            mIpManager = null;
147        }
148        // ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object
149        // with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here:
150        // that sets the state to IDLE, and ConnectivityService will still think we're connected.
151        //
152        mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
153        if (mNetworkAgent != null) {
154            updateAgent();
155            mNetworkAgent = null;
156        }
157        clearInfo();
158    }
159
160    /**
161     * Updates interface state variables.
162     * Called on link state changes or on startup.
163     */
164    private void updateInterfaceState(String iface, boolean up) {
165        if (!mIface.equals(iface)) {
166            return;
167        }
168        Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down"));
169
170        mLinkUp = up;
171        if (up) {
172            maybeStartIpManager();
173        } else {
174            stopIpManager();
175        }
176    }
177
178    private class InterfaceObserver extends BaseNetworkObserver {
179        @Override
180        public void interfaceLinkStateChanged(String iface, boolean up) {
181            mHandler.post(() -> {
182                updateInterfaceState(iface, up);
183            });
184        }
185
186        @Override
187        public void interfaceAdded(String iface) {
188            mHandler.post(() -> {
189                maybeTrackInterface(iface);
190            });
191        }
192
193        @Override
194        public void interfaceRemoved(String iface) {
195            mHandler.post(() -> {
196                if (stopTrackingInterface(iface)) {
197                    trackFirstAvailableInterface();
198                }
199            });
200        }
201    }
202
203    private void setInterfaceUp(String iface) {
204        // Bring up the interface so we get link status indications.
205        try {
206            mNMService.setInterfaceUp(iface);
207            String hwAddr = null;
208            InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
209
210            if (config == null) {
211                Log.e(TAG, "Null interface config for " + iface + ". Bailing out.");
212                return;
213            }
214
215            if (!isTrackingInterface()) {
216                setInterfaceInfo(iface, config.getHardwareAddress());
217                mNetworkInfo.setIsAvailable(true);
218                mNetworkInfo.setExtraInfo(mHwAddr);
219            } else {
220                Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface);
221                mNMService.setInterfaceDown(iface);
222            }
223        } catch (RemoteException | IllegalStateException e) {
224            // Either the system is crashing or the interface has disappeared. Just ignore the
225            // error; we haven't modified any state because we only do that if our calls succeed.
226            Log.e(TAG, "Error upping interface " + mIface + ": " + e);
227        }
228    }
229
230    private boolean maybeTrackInterface(String iface) {
231        // If we don't already have an interface, and if this interface matches
232        // our regex, start tracking it.
233        if (!iface.matches(mIfaceMatch) || isTrackingInterface())
234            return false;
235
236        Log.d(TAG, "Started tracking interface " + iface);
237        setInterfaceUp(iface);
238        return true;
239    }
240
241    private boolean stopTrackingInterface(String iface) {
242        if (!iface.equals(mIface))
243            return false;
244
245        Log.d(TAG, "Stopped tracking interface " + iface);
246        setInterfaceInfo("", null);
247        stopIpManager();
248        return true;
249    }
250
251    private boolean setStaticIpAddress(StaticIpConfiguration staticConfig) {
252        if (staticConfig.ipAddress != null &&
253                staticConfig.gateway != null &&
254                staticConfig.dnsServers.size() > 0) {
255            try {
256                Log.i(TAG, "Applying static IPv4 configuration to " + mIface + ": " + staticConfig);
257                InterfaceConfiguration config = mNMService.getInterfaceConfig(mIface);
258                config.setLinkAddress(staticConfig.ipAddress);
259                mNMService.setInterfaceConfig(mIface, config);
260                return true;
261            } catch(RemoteException|IllegalStateException e) {
262               Log.e(TAG, "Setting static IP address failed: " + e.getMessage());
263            }
264        } else {
265            Log.e(TAG, "Invalid static IP configuration.");
266        }
267        return false;
268    }
269
270    public void updateAgent() {
271        if (mNetworkAgent == null) return;
272        if (DBG) {
273            Log.i(TAG, "Updating mNetworkAgent with: " +
274                  mNetworkCapabilities + ", " +
275                  mNetworkInfo + ", " +
276                  mLinkProperties);
277        }
278        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
279        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
280        mNetworkAgent.sendLinkProperties(mLinkProperties);
281        // never set the network score below 0.
282        mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0);
283    }
284
285    void onIpLayerStarted(LinkProperties linkProperties) {
286        if (mNetworkAgent != null) {
287            Log.e(TAG, "Already have a NetworkAgent - aborting new request");
288            stopIpManager();
289            return;
290        }
291        mLinkProperties = linkProperties;
292        mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
293
294        // Create our NetworkAgent.
295        mNetworkAgent = new NetworkAgent(mHandler.getLooper(), mContext,
296                NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties,
297                NETWORK_SCORE) {
298            public void unwanted() {
299                if (this == mNetworkAgent) {
300                    stopIpManager();
301                } else if (mNetworkAgent != null) {
302                    Log.d(TAG, "Ignoring unwanted as we have a more modern " +
303                            "instance");
304                }  // Otherwise, we've already called stopIpManager.
305            }
306        };
307    }
308
309    void onIpLayerStopped(LinkProperties linkProperties) {
310        // This cannot happen due to provisioning timeout, because our timeout is 0. It can only
311        // happen if we're provisioned and we lose provisioning.
312        stopIpManager();
313        maybeStartIpManager();
314    }
315
316    void updateLinkProperties(LinkProperties linkProperties) {
317        mLinkProperties = linkProperties;
318        if (mNetworkAgent != null) {
319            mNetworkAgent.sendLinkProperties(linkProperties);
320        }
321    }
322
323    public void maybeStartIpManager() {
324        if (mNetworkRequested && mIpManager == null && isTrackingInterface()) {
325            startIpManager();
326        }
327    }
328
329    public void startIpManager() {
330        if (DBG) {
331            Log.d(TAG, String.format("starting IpManager(%s): mNetworkInfo=%s", mIface,
332                    mNetworkInfo));
333        }
334
335        LinkProperties linkProperties;
336
337        IpConfiguration config = mEthernetManager.getConfiguration();
338
339        if (config.getIpAssignment() == IpAssignment.STATIC) {
340            if (!setStaticIpAddress(config.getStaticIpConfiguration())) {
341                // We've already logged an error.
342                return;
343            }
344            linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface);
345        } else {
346            mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
347            IpManager.Callback ipmCallback = new IpManager.Callback() {
348                @Override
349                public void onProvisioningSuccess(LinkProperties newLp) {
350                    mHandler.post(() -> onIpLayerStarted(newLp));
351                }
352
353                @Override
354                public void onProvisioningFailure(LinkProperties newLp) {
355                    mHandler.post(() -> onIpLayerStopped(newLp));
356                }
357
358                @Override
359                public void onLinkPropertiesChange(LinkProperties newLp) {
360                    mHandler.post(() -> updateLinkProperties(newLp));
361                }
362            };
363
364            stopIpManager();
365            mIpManager = new IpManager(mContext, mIface, ipmCallback);
366
367            if (config.getProxySettings() == ProxySettings.STATIC ||
368                    config.getProxySettings() == ProxySettings.PAC) {
369                mIpManager.setHttpProxy(config.getHttpProxy());
370            }
371
372            final String tcpBufferSizes = mContext.getResources().getString(
373                    com.android.internal.R.string.config_ethernet_tcp_buffers);
374            if (!TextUtils.isEmpty(tcpBufferSizes)) {
375                mIpManager.setTcpBufferSizes(tcpBufferSizes);
376            }
377
378            final ProvisioningConfiguration provisioningConfiguration =
379                    mIpManager.buildProvisioningConfiguration()
380                            .withProvisioningTimeoutMs(0)
381                            .build();
382            mIpManager.startProvisioning(provisioningConfiguration);
383        }
384    }
385
386    /**
387     * Begin monitoring connectivity
388     */
389    public void start(Context context, Handler handler) {
390        mHandler = handler;
391
392        // The services we use.
393        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
394        mNMService = INetworkManagementService.Stub.asInterface(b);
395        mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE);
396
397        // Interface match regex.
398        mIfaceMatch = context.getResources().getString(
399                com.android.internal.R.string.config_ethernet_iface_regex);
400
401        // Create and register our NetworkFactory.
402        mFactory = new LocalNetworkFactory(NETWORK_TYPE, context, mHandler.getLooper());
403        mFactory.setCapabilityFilter(mNetworkCapabilities);
404        mFactory.setScoreFilter(NETWORK_SCORE);
405        mFactory.register();
406
407        mContext = context;
408
409        // Start tracking interface change events.
410        mInterfaceObserver = new InterfaceObserver();
411        try {
412            mNMService.registerObserver(mInterfaceObserver);
413        } catch (RemoteException e) {
414            Log.e(TAG, "Could not register InterfaceObserver " + e);
415        }
416
417        // If an Ethernet interface is already connected, start tracking that.
418        // Otherwise, the first Ethernet interface to appear will be tracked.
419        mHandler.post(() -> trackFirstAvailableInterface());
420    }
421
422    public void trackFirstAvailableInterface() {
423        try {
424            final String[] ifaces = mNMService.listInterfaces();
425            for (String iface : ifaces) {
426                if (maybeTrackInterface(iface)) {
427                    // We have our interface. Track it.
428                    // Note: if the interface already has link (e.g., if we crashed and got
429                    // restarted while it was running), we need to fake a link up notification so we
430                    // start configuring it.
431                    if (mNMService.getInterfaceConfig(iface).hasFlag("running")) {
432                        updateInterfaceState(iface, true);
433                    }
434                    break;
435                }
436            }
437        } catch (RemoteException|IllegalStateException e) {
438            Log.e(TAG, "Could not get list of interfaces " + e);
439        }
440    }
441
442    public void stop() {
443        stopIpManager();
444        setInterfaceInfo("", null);
445        mFactory.unregister();
446    }
447
448    private void initNetworkCapabilities() {
449        mNetworkCapabilities = new NetworkCapabilities();
450        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET);
451        mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
452        mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
453        // We have no useful data on bandwidth. Say 100M up and 100M down. :-(
454        mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100 * 1000);
455        mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000);
456    }
457
458    public boolean isTrackingInterface() {
459        return !TextUtils.isEmpty(mIface);
460    }
461
462    /**
463     * Set interface information and notify listeners if availability is changed.
464     */
465    private void setInterfaceInfo(String iface, String hwAddr) {
466        boolean oldAvailable = isTrackingInterface();
467        mIface = iface;
468        mHwAddr = hwAddr;
469        boolean available = isTrackingInterface();
470
471        mNetworkInfo.setExtraInfo(mHwAddr);
472        mNetworkInfo.setIsAvailable(available);
473
474        if (oldAvailable != available) {
475            int n = mListeners.beginBroadcast();
476            for (int i = 0; i < n; i++) {
477                try {
478                    mListeners.getBroadcastItem(i).onAvailabilityChanged(available);
479                } catch (RemoteException e) {
480                    // Do nothing here.
481                }
482            }
483            mListeners.finishBroadcast();
484        }
485    }
486
487    private void postAndWaitForRunnable(Runnable r) throws InterruptedException {
488        CountDownLatch latch = new CountDownLatch(1);
489        mHandler.post(() -> {
490            try {
491                r.run();
492            } finally {
493                latch.countDown();
494            }
495        });
496        latch.await();
497    }
498
499
500    void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
501        try {
502            postAndWaitForRunnable(() -> {
503                pw.println("Network Requested: " + mNetworkRequested);
504                if (isTrackingInterface()) {
505                    pw.println("Tracking interface: " + mIface);
506                    pw.increaseIndent();
507                    pw.println("MAC address: " + mHwAddr);
508                    pw.println("Link state: " + (mLinkUp ? "up" : "down"));
509                    pw.decreaseIndent();
510                } else {
511                    pw.println("Not tracking any interface");
512                }
513
514                pw.println();
515                pw.println("NetworkInfo: " + mNetworkInfo);
516                pw.println("LinkProperties: " + mLinkProperties);
517                pw.println("NetworkAgent: " + mNetworkAgent);
518                if (mIpManager != null) {
519                    pw.println("IpManager:");
520                    pw.increaseIndent();
521                    mIpManager.dump(fd, pw, args);
522                    pw.decreaseIndent();
523                }
524            });
525        } catch (InterruptedException e) {
526            throw new IllegalStateException("dump() interrupted");
527        }
528    }
529}
530