1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.ethernet;
18 
19 import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
20 
21 import android.annotation.Nullable;
22 import android.content.Context;
23 import android.net.IEthernetServiceListener;
24 import android.net.ITetheredInterfaceCallback;
25 import android.net.InterfaceConfiguration;
26 import android.net.IpConfiguration;
27 import android.net.IpConfiguration.IpAssignment;
28 import android.net.IpConfiguration.ProxySettings;
29 import android.net.LinkAddress;
30 import android.net.NetworkCapabilities;
31 import android.net.StaticIpConfiguration;
32 import android.os.Handler;
33 import android.os.IBinder;
34 import android.os.INetworkManagementService;
35 import android.os.RemoteCallbackList;
36 import android.os.RemoteException;
37 import android.os.ServiceManager;
38 import android.text.TextUtils;
39 import android.util.ArrayMap;
40 import android.util.Log;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.util.IndentingPrintWriter;
44 import com.android.server.net.BaseNetworkObserver;
45 
46 import java.io.FileDescriptor;
47 import java.net.InetAddress;
48 import java.util.ArrayList;
49 import java.util.concurrent.ConcurrentHashMap;
50 
51 /**
52  * Tracks Ethernet interfaces and manages interface configurations.
53  *
54  * <p>Interfaces may have different {@link android.net.NetworkCapabilities}. This mapping is defined
55  * in {@code config_ethernet_interfaces}. Notably, some interfaces could be marked as restricted by
56  * not specifying {@link android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED} flag.
57  * Interfaces could have associated {@link android.net.IpConfiguration}.
58  * Ethernet Interfaces may be present at boot time or appear after boot (e.g., for Ethernet adapters
59  * connected over USB). This class supports multiple interfaces. When an interface appears on the
60  * system (or is present at boot time) this class will start tracking it and bring it up. Only
61  * interfaces whose names match the {@code config_ethernet_iface_regex} regular expression are
62  * tracked.
63  *
64  * <p>All public or package private methods must be thread-safe unless stated otherwise.
65  */
66 final class EthernetTracker {
67     private static final int INTERFACE_MODE_CLIENT = 1;
68     private static final int INTERFACE_MODE_SERVER = 2;
69 
70     private final static String TAG = EthernetTracker.class.getSimpleName();
71     private final static boolean DBG = EthernetNetworkFactory.DBG;
72 
73     private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+";
74 
75     /**
76      * Interface names we track. This is a product-dependent regular expression, plus,
77      * if setIncludeTestInterfaces is true, any test interfaces.
78      */
79     private String mIfaceMatch;
80     private boolean mIncludeTestInterfaces = false;
81 
82     /** Mapping between {iface name | mac address} -> {NetworkCapabilities} */
83     private final ConcurrentHashMap<String, NetworkCapabilities> mNetworkCapabilities =
84             new ConcurrentHashMap<>();
85     private final ConcurrentHashMap<String, IpConfiguration> mIpConfigurations =
86             new ConcurrentHashMap<>();
87 
88     private final Context mContext;
89     private final INetworkManagementService mNMService;
90     private final Handler mHandler;
91     private final EthernetNetworkFactory mFactory;
92     private final EthernetConfigStore mConfigStore;
93 
94     private final RemoteCallbackList<IEthernetServiceListener> mListeners =
95             new RemoteCallbackList<>();
96     private final TetheredInterfaceRequestList mTetheredInterfaceRequests =
97             new TetheredInterfaceRequestList();
98 
99     // Used only on the handler thread
100     private String mDefaultInterface;
101     private int mDefaultInterfaceMode = INTERFACE_MODE_CLIENT;
102     // Tracks whether clients were notified that the tethered interface is available
103     private boolean mTetheredInterfaceWasAvailable = false;
104     private volatile IpConfiguration mIpConfigForDefaultInterface;
105 
106     private class TetheredInterfaceRequestList extends RemoteCallbackList<ITetheredInterfaceCallback> {
107         @Override
onCallbackDied(ITetheredInterfaceCallback cb, Object cookie)108         public void onCallbackDied(ITetheredInterfaceCallback cb, Object cookie) {
109             mHandler.post(EthernetTracker.this::maybeUntetherDefaultInterface);
110         }
111     }
112 
EthernetTracker(Context context, Handler handler)113     EthernetTracker(Context context, Handler handler) {
114         mContext = context;
115         mHandler = handler;
116 
117         // The services we use.
118         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
119         mNMService = INetworkManagementService.Stub.asInterface(b);
120 
121         // Interface match regex.
122         updateIfaceMatchRegexp();
123 
124         // Read default Ethernet interface configuration from resources
125         final String[] interfaceConfigs = context.getResources().getStringArray(
126                 com.android.internal.R.array.config_ethernet_interfaces);
127         for (String strConfig : interfaceConfigs) {
128             parseEthernetConfig(strConfig);
129         }
130 
131         mConfigStore = new EthernetConfigStore();
132 
133         NetworkCapabilities nc = createNetworkCapabilities(true /* clear default capabilities */);
134         mFactory = new EthernetNetworkFactory(handler, context, nc);
135         mFactory.register();
136     }
137 
start()138     void start() {
139         mConfigStore.read();
140 
141         // Default interface is just the first one we want to track.
142         mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface();
143         final ArrayMap<String, IpConfiguration> configs = mConfigStore.getIpConfigurations();
144         for (int i = 0; i < configs.size(); i++) {
145             mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i));
146         }
147 
148         try {
149             mNMService.registerObserver(new InterfaceObserver());
150         } catch (RemoteException e) {
151             Log.e(TAG, "Could not register InterfaceObserver " + e);
152         }
153 
154         mHandler.post(this::trackAvailableInterfaces);
155     }
156 
updateIpConfiguration(String iface, IpConfiguration ipConfiguration)157     void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) {
158         if (DBG) {
159             Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration);
160         }
161 
162         mConfigStore.write(iface, ipConfiguration);
163         mIpConfigurations.put(iface, ipConfiguration);
164 
165         mHandler.post(() -> mFactory.updateIpConfiguration(iface, ipConfiguration));
166     }
167 
getIpConfiguration(String iface)168     IpConfiguration getIpConfiguration(String iface) {
169         return mIpConfigurations.get(iface);
170     }
171 
isTrackingInterface(String iface)172     boolean isTrackingInterface(String iface) {
173         return mFactory.hasInterface(iface);
174     }
175 
getInterfaces(boolean includeRestricted)176     String[] getInterfaces(boolean includeRestricted) {
177         return mFactory.getAvailableInterfaces(includeRestricted);
178     }
179 
180     /**
181      * Returns true if given interface was configured as restricted (doesn't have
182      * NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false.
183      */
isRestrictedInterface(String iface)184     boolean isRestrictedInterface(String iface) {
185         final NetworkCapabilities nc = mNetworkCapabilities.get(iface);
186         return nc != null && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
187     }
188 
addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks)189     void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) {
190         mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks));
191     }
192 
removeListener(IEthernetServiceListener listener)193     void removeListener(IEthernetServiceListener listener) {
194         mListeners.unregister(listener);
195     }
196 
setIncludeTestInterfaces(boolean include)197     public void setIncludeTestInterfaces(boolean include) {
198         mHandler.post(() -> {
199             mIncludeTestInterfaces = include;
200             updateIfaceMatchRegexp();
201             mHandler.post(() -> trackAvailableInterfaces());
202         });
203     }
204 
requestTetheredInterface(ITetheredInterfaceCallback callback)205     public void requestTetheredInterface(ITetheredInterfaceCallback callback) {
206         mHandler.post(() -> {
207             if (!mTetheredInterfaceRequests.register(callback)) {
208                 // Remote process has already died
209                 return;
210             }
211             if (mDefaultInterfaceMode == INTERFACE_MODE_SERVER) {
212                 if (mTetheredInterfaceWasAvailable) {
213                     notifyTetheredInterfaceAvailable(callback, mDefaultInterface);
214                 }
215                 return;
216             }
217 
218             setDefaultInterfaceMode(INTERFACE_MODE_SERVER);
219         });
220     }
221 
releaseTetheredInterface(ITetheredInterfaceCallback callback)222     public void releaseTetheredInterface(ITetheredInterfaceCallback callback) {
223         mHandler.post(() -> {
224             mTetheredInterfaceRequests.unregister(callback);
225             maybeUntetherDefaultInterface();
226         });
227     }
228 
notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface)229     private void notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface) {
230         try {
231             cb.onAvailable(iface);
232         } catch (RemoteException e) {
233             Log.e(TAG, "Error sending tethered interface available callback", e);
234         }
235     }
236 
notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb)237     private void notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb) {
238         try {
239             cb.onUnavailable();
240         } catch (RemoteException e) {
241             Log.e(TAG, "Error sending tethered interface available callback", e);
242         }
243     }
244 
maybeUntetherDefaultInterface()245     private void maybeUntetherDefaultInterface() {
246         if (mTetheredInterfaceRequests.getRegisteredCallbackCount() > 0) return;
247         if (mDefaultInterfaceMode == INTERFACE_MODE_CLIENT) return;
248         setDefaultInterfaceMode(INTERFACE_MODE_CLIENT);
249     }
250 
setDefaultInterfaceMode(int mode)251     private void setDefaultInterfaceMode(int mode) {
252         Log.d(TAG, "Setting default interface mode to " + mode);
253         mDefaultInterfaceMode = mode;
254         if (mDefaultInterface != null) {
255             removeInterface(mDefaultInterface);
256             addInterface(mDefaultInterface);
257         }
258     }
259 
getInterfaceMode(final String iface)260     private int getInterfaceMode(final String iface) {
261         if (iface.equals(mDefaultInterface)) {
262             return mDefaultInterfaceMode;
263         }
264         return INTERFACE_MODE_CLIENT;
265     }
266 
removeInterface(String iface)267     private void removeInterface(String iface) {
268         mFactory.removeInterface(iface);
269         maybeUpdateServerModeInterfaceState(iface, false);
270     }
271 
stopTrackingInterface(String iface)272     private void stopTrackingInterface(String iface) {
273         removeInterface(iface);
274         if (iface.equals(mDefaultInterface)) {
275             mDefaultInterface = null;
276         }
277     }
278 
addInterface(String iface)279     private void addInterface(String iface) {
280         InterfaceConfiguration config = null;
281         // Bring up the interface so we get link status indications.
282         try {
283             mNMService.setInterfaceUp(iface);
284             config = mNMService.getInterfaceConfig(iface);
285         } catch (RemoteException | IllegalStateException e) {
286             // Either the system is crashing or the interface has disappeared. Just ignore the
287             // error; we haven't modified any state because we only do that if our calls succeed.
288             Log.e(TAG, "Error upping interface " + iface, e);
289         }
290 
291         if (config == null) {
292             Log.e(TAG, "Null interface config for " + iface + ". Bailing out.");
293             return;
294         }
295 
296         final String hwAddress = config.getHardwareAddress();
297 
298         NetworkCapabilities nc = mNetworkCapabilities.get(iface);
299         if (nc == null) {
300             // Try to resolve using mac address
301             nc = mNetworkCapabilities.get(hwAddress);
302             if (nc == null) {
303                 final boolean isTestIface = iface.matches(TEST_IFACE_REGEXP);
304                 nc = createDefaultNetworkCapabilities(isTestIface);
305             }
306         }
307 
308         final int mode = getInterfaceMode(iface);
309         if (mode == INTERFACE_MODE_CLIENT) {
310             IpConfiguration ipConfiguration = mIpConfigurations.get(iface);
311             if (ipConfiguration == null) {
312                 ipConfiguration = createDefaultIpConfiguration();
313             }
314 
315             Log.d(TAG, "Tracking interface in client mode: " + iface);
316             mFactory.addInterface(iface, hwAddress, nc, ipConfiguration);
317         } else {
318             maybeUpdateServerModeInterfaceState(iface, true);
319         }
320 
321         // Note: if the interface already has link (e.g., if we crashed and got
322         // restarted while it was running), we need to fake a link up notification so we
323         // start configuring it.
324         if (config.hasFlag("running")) {
325             updateInterfaceState(iface, true);
326         }
327     }
328 
updateInterfaceState(String iface, boolean up)329     private void updateInterfaceState(String iface, boolean up) {
330         final int mode = getInterfaceMode(iface);
331         final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT)
332                 && mFactory.updateInterfaceLinkState(iface, up);
333 
334         if (factoryLinkStateUpdated) {
335             boolean restricted = isRestrictedInterface(iface);
336             int n = mListeners.beginBroadcast();
337             for (int i = 0; i < n; i++) {
338                 try {
339                     if (restricted) {
340                         ListenerInfo listenerInfo = (ListenerInfo) mListeners.getBroadcastCookie(i);
341                         if (!listenerInfo.canUseRestrictedNetworks) {
342                             continue;
343                         }
344                     }
345                     mListeners.getBroadcastItem(i).onAvailabilityChanged(iface, up);
346                 } catch (RemoteException e) {
347                     // Do nothing here.
348                 }
349             }
350             mListeners.finishBroadcast();
351         }
352     }
353 
maybeUpdateServerModeInterfaceState(String iface, boolean available)354     private void maybeUpdateServerModeInterfaceState(String iface, boolean available) {
355         if (available == mTetheredInterfaceWasAvailable || !iface.equals(mDefaultInterface)) return;
356 
357         Log.d(TAG, (available ? "Tracking" : "No longer tracking")
358                 + " interface in server mode: " + iface);
359 
360         final int pendingCbs = mTetheredInterfaceRequests.beginBroadcast();
361         for (int i = 0; i < pendingCbs; i++) {
362             ITetheredInterfaceCallback item = mTetheredInterfaceRequests.getBroadcastItem(i);
363             if (available) {
364                 notifyTetheredInterfaceAvailable(item, iface);
365             } else {
366                 notifyTetheredInterfaceUnavailable(item);
367             }
368         }
369         mTetheredInterfaceRequests.finishBroadcast();
370         mTetheredInterfaceWasAvailable = available;
371     }
372 
maybeTrackInterface(String iface)373     private void maybeTrackInterface(String iface) {
374         if (!iface.matches(mIfaceMatch)) {
375             return;
376         }
377 
378         // If we don't already track this interface, and if this interface matches
379         // our regex, start tracking it.
380         if (mFactory.hasInterface(iface) || iface.equals(mDefaultInterface)) {
381             if (DBG) Log.w(TAG, "Ignoring already-tracked interface " + iface);
382             return;
383         }
384         if (DBG) Log.i(TAG, "maybeTrackInterface: " + iface);
385 
386         // TODO: avoid making an interface default if it has configured NetworkCapabilities.
387         if (mDefaultInterface == null) {
388             mDefaultInterface = iface;
389         }
390 
391         if (mIpConfigForDefaultInterface != null) {
392             updateIpConfiguration(iface, mIpConfigForDefaultInterface);
393             mIpConfigForDefaultInterface = null;
394         }
395 
396         addInterface(iface);
397     }
398 
trackAvailableInterfaces()399     private void trackAvailableInterfaces() {
400         try {
401             final String[] ifaces = mNMService.listInterfaces();
402             for (String iface : ifaces) {
403                 maybeTrackInterface(iface);
404             }
405         } catch (RemoteException | IllegalStateException e) {
406             Log.e(TAG, "Could not get list of interfaces " + e);
407         }
408     }
409 
410 
411     private class InterfaceObserver extends BaseNetworkObserver {
412 
413         @Override
interfaceLinkStateChanged(String iface, boolean up)414         public void interfaceLinkStateChanged(String iface, boolean up) {
415             if (DBG) {
416                 Log.i(TAG, "interfaceLinkStateChanged, iface: " + iface + ", up: " + up);
417             }
418             mHandler.post(() -> updateInterfaceState(iface, up));
419         }
420 
421         @Override
interfaceAdded(String iface)422         public void interfaceAdded(String iface) {
423             mHandler.post(() -> maybeTrackInterface(iface));
424         }
425 
426         @Override
interfaceRemoved(String iface)427         public void interfaceRemoved(String iface) {
428             mHandler.post(() -> stopTrackingInterface(iface));
429         }
430     }
431 
432     private static class ListenerInfo {
433 
434         boolean canUseRestrictedNetworks = false;
435 
ListenerInfo(boolean canUseRestrictedNetworks)436         ListenerInfo(boolean canUseRestrictedNetworks) {
437             this.canUseRestrictedNetworks = canUseRestrictedNetworks;
438         }
439     }
440 
441     /**
442      * Parses an Ethernet interface configuration
443      *
444      * @param configString represents an Ethernet configuration in the following format: {@code
445      * <interface name|mac address>;[Network Capabilities];[IP config];[Override Transport]}
446      */
parseEthernetConfig(String configString)447     private void parseEthernetConfig(String configString) {
448         String[] tokens = configString.split(";", /* limit of tokens */ 4);
449         String name = tokens[0];
450         String capabilities = tokens.length > 1 ? tokens[1] : null;
451         String transport = tokens.length > 3 ? tokens[3] : null;
452         NetworkCapabilities nc = createNetworkCapabilities(
453                 !TextUtils.isEmpty(capabilities)  /* clear default capabilities */, capabilities,
454                 transport);
455         mNetworkCapabilities.put(name, nc);
456 
457         if (tokens.length > 2 && !TextUtils.isEmpty(tokens[2])) {
458             IpConfiguration ipConfig = parseStaticIpConfiguration(tokens[2]);
459             mIpConfigurations.put(name, ipConfig);
460         }
461     }
462 
createDefaultNetworkCapabilities(boolean isTestIface)463     private static NetworkCapabilities createDefaultNetworkCapabilities(boolean isTestIface) {
464         NetworkCapabilities nc = createNetworkCapabilities(false /* clear default capabilities */);
465         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
466         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
467         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
468         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
469         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
470 
471         if (isTestIface) {
472             nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
473         } else {
474             nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
475         }
476 
477         return nc;
478     }
479 
createNetworkCapabilities(boolean clearDefaultCapabilities)480     private static NetworkCapabilities createNetworkCapabilities(boolean clearDefaultCapabilities) {
481         return createNetworkCapabilities(clearDefaultCapabilities, null, null);
482     }
483 
484     /**
485      * Parses a static list of network capabilities
486      *
487      * @param clearDefaultCapabilities Indicates whether or not to clear any default capabilities
488      * @param commaSeparatedCapabilities A comma separated string list of integer encoded
489      *                                   NetworkCapability.NET_CAPABILITY_* values
490      * @param overrideTransport A string representing a single integer encoded override transport
491      *                          type. Must be one of the NetworkCapability.TRANSPORT_*
492      *                          values. TRANSPORT_VPN is not supported. Errors with input
493      *                          will cause the override to be ignored.
494      */
495     @VisibleForTesting
createNetworkCapabilities( boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities, @Nullable String overrideTransport)496     static NetworkCapabilities createNetworkCapabilities(
497             boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities,
498             @Nullable String overrideTransport) {
499 
500         NetworkCapabilities nc = new NetworkCapabilities();
501         if (clearDefaultCapabilities) {
502             nc.clearAll();  // Remove default capabilities and transports
503         }
504 
505         // Determine the transport type. If someone has tried to define an override transport then
506         // attempt to add it. Since we can only have one override, all errors with it will
507         // gracefully default back to TRANSPORT_ETHERNET and warn the user. VPN is not allowed as an
508         // override type. Wifi Aware and LoWPAN are currently unsupported as well.
509         int transport = NetworkCapabilities.TRANSPORT_ETHERNET;
510         if (!TextUtils.isEmpty(overrideTransport)) {
511             try {
512                 int parsedTransport = Integer.valueOf(overrideTransport);
513                 if (parsedTransport == NetworkCapabilities.TRANSPORT_VPN
514                         || parsedTransport == NetworkCapabilities.TRANSPORT_WIFI_AWARE
515                         || parsedTransport == NetworkCapabilities.TRANSPORT_LOWPAN) {
516                     Log.e(TAG, "Override transport '" + parsedTransport + "' is not supported. "
517                             + "Defaulting to TRANSPORT_ETHERNET");
518                 } else {
519                     transport = parsedTransport;
520                 }
521             } catch (NumberFormatException nfe) {
522                 Log.e(TAG, "Override transport type '" + overrideTransport + "' "
523                         + "could not be parsed. Defaulting to TRANSPORT_ETHERNET");
524             }
525         }
526 
527         // Apply the transport. If the user supplied a valid number that is not a valid transport
528         // then adding will throw an exception. Default back to TRANSPORT_ETHERNET if that happens
529         try {
530             nc.addTransportType(transport);
531         } catch (IllegalArgumentException iae) {
532             Log.e(TAG, transport + " is not a valid NetworkCapability.TRANSPORT_* value. "
533                     + "Defaulting to TRANSPORT_ETHERNET");
534             nc.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET);
535         }
536 
537         nc.setLinkUpstreamBandwidthKbps(100 * 1000);
538         nc.setLinkDownstreamBandwidthKbps(100 * 1000);
539 
540         if (!TextUtils.isEmpty(commaSeparatedCapabilities)) {
541             for (String strNetworkCapability : commaSeparatedCapabilities.split(",")) {
542                 if (!TextUtils.isEmpty(strNetworkCapability)) {
543                     try {
544                         nc.addCapability(Integer.valueOf(strNetworkCapability));
545                     } catch (NumberFormatException nfe) {
546                         Log.e(TAG, "Capability '" + strNetworkCapability + "' could not be parsed");
547                     } catch (IllegalArgumentException iae) {
548                         Log.e(TAG, strNetworkCapability + " is not a valid "
549                                 + "NetworkCapability.NET_CAPABILITY_* value");
550                     }
551                 }
552             }
553         }
554         // Ethernet networks have no way to update the following capabilities, so they always
555         // have them.
556         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
557         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
558         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
559 
560         return nc;
561     }
562 
563     /**
564      * Parses static IP configuration.
565      *
566      * @param staticIpConfig represents static IP configuration in the following format: {@code
567      * ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
568      *     domains=<comma-sep-domains>}
569      */
570     @VisibleForTesting
parseStaticIpConfiguration(String staticIpConfig)571     static IpConfiguration parseStaticIpConfiguration(String staticIpConfig) {
572         StaticIpConfiguration ipConfig = new StaticIpConfiguration();
573 
574         for (String keyValueAsString : staticIpConfig.trim().split(" ")) {
575             if (TextUtils.isEmpty(keyValueAsString)) continue;
576 
577             String[] pair = keyValueAsString.split("=");
578             if (pair.length != 2) {
579                 throw new IllegalArgumentException("Unexpected token: " + keyValueAsString
580                         + " in " + staticIpConfig);
581             }
582 
583             String key = pair[0];
584             String value = pair[1];
585 
586             switch (key) {
587                 case "ip":
588                     ipConfig.ipAddress = new LinkAddress(value);
589                     break;
590                 case "domains":
591                     ipConfig.domains = value;
592                     break;
593                 case "gateway":
594                     ipConfig.gateway = InetAddress.parseNumericAddress(value);
595                     break;
596                 case "dns": {
597                     ArrayList<InetAddress> dnsAddresses = new ArrayList<>();
598                     for (String address: value.split(",")) {
599                         dnsAddresses.add(InetAddress.parseNumericAddress(address));
600                     }
601                     ipConfig.dnsServers.addAll(dnsAddresses);
602                     break;
603                 }
604                 default : {
605                     throw new IllegalArgumentException("Unexpected key: " + key
606                             + " in " + staticIpConfig);
607                 }
608             }
609         }
610         return new IpConfiguration(IpAssignment.STATIC, ProxySettings.NONE, ipConfig, null);
611     }
612 
createDefaultIpConfiguration()613     private static IpConfiguration createDefaultIpConfiguration() {
614         return new IpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null);
615     }
616 
updateIfaceMatchRegexp()617     private void updateIfaceMatchRegexp() {
618         final String match = mContext.getResources().getString(
619                 com.android.internal.R.string.config_ethernet_iface_regex);
620         mIfaceMatch = mIncludeTestInterfaces
621                 ? "(" + match + "|" + TEST_IFACE_REGEXP + ")"
622                 : match;
623         Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'");
624     }
625 
postAndWaitForRunnable(Runnable r)626     private void postAndWaitForRunnable(Runnable r) {
627         mHandler.runWithScissors(r, 2000L /* timeout */);
628     }
629 
dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args)630     void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
631         postAndWaitForRunnable(() -> {
632             pw.println(getClass().getSimpleName());
633             pw.println("Ethernet interface name filter: " + mIfaceMatch);
634             pw.println("Default interface: " + mDefaultInterface);
635             pw.println("Default interface mode: " + mDefaultInterfaceMode);
636             pw.println("Tethered interface requests: "
637                     + mTetheredInterfaceRequests.getRegisteredCallbackCount());
638             pw.println("Listeners: " + mListeners.getRegisteredCallbackCount());
639             pw.println("IP Configurations:");
640             pw.increaseIndent();
641             for (String iface : mIpConfigurations.keySet()) {
642                 pw.println(iface + ": " + mIpConfigurations.get(iface));
643             }
644             pw.decreaseIndent();
645             pw.println();
646 
647             pw.println("Network Capabilities:");
648             pw.increaseIndent();
649             for (String iface : mNetworkCapabilities.keySet()) {
650                 pw.println(iface + ": " + mNetworkCapabilities.get(iface));
651             }
652             pw.decreaseIndent();
653             pw.println();
654 
655             mFactory.dump(fd, pw, args);
656         });
657     }
658 }
659