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