• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.EthernetManager.ETHERNET_STATE_DISABLED;
20 import static android.net.EthernetManager.ETHERNET_STATE_ENABLED;
21 import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
22 
23 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.content.Context;
28 import android.net.ConnectivityResources;
29 import android.net.EthernetManager;
30 import android.net.IEthernetServiceListener;
31 import android.net.INetd;
32 import android.net.INetworkInterfaceOutcomeReceiver;
33 import android.net.ITetheredInterfaceCallback;
34 import android.net.InterfaceConfigurationParcel;
35 import android.net.IpConfiguration;
36 import android.net.IpConfiguration.IpAssignment;
37 import android.net.IpConfiguration.ProxySettings;
38 import android.net.LinkAddress;
39 import android.net.NetworkCapabilities;
40 import android.net.StaticIpConfiguration;
41 import android.os.ConditionVariable;
42 import android.os.Handler;
43 import android.os.RemoteCallbackList;
44 import android.os.RemoteException;
45 import android.os.ServiceSpecificException;
46 import android.text.TextUtils;
47 import android.util.ArrayMap;
48 import android.util.Log;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.util.IndentingPrintWriter;
52 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
53 import com.android.net.module.util.NetdUtils;
54 import com.android.net.module.util.PermissionUtils;
55 
56 import java.io.FileDescriptor;
57 import java.net.InetAddress;
58 import java.util.ArrayList;
59 import java.util.Iterator;
60 import java.util.List;
61 import java.util.Objects;
62 import java.util.concurrent.ConcurrentHashMap;
63 
64 /**
65  * Tracks Ethernet interfaces and manages interface configurations.
66  *
67  * <p>Interfaces may have different {@link android.net.NetworkCapabilities}. This mapping is defined
68  * in {@code config_ethernet_interfaces}. Notably, some interfaces could be marked as restricted by
69  * not specifying {@link android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED} flag.
70  * Interfaces could have associated {@link android.net.IpConfiguration}.
71  * Ethernet Interfaces may be present at boot time or appear after boot (e.g., for Ethernet adapters
72  * connected over USB). This class supports multiple interfaces. When an interface appears on the
73  * system (or is present at boot time) this class will start tracking it and bring it up. Only
74  * interfaces whose names match the {@code config_ethernet_iface_regex} regular expression are
75  * tracked.
76  *
77  * <p>All public or package private methods must be thread-safe unless stated otherwise.
78  */
79 @VisibleForTesting(visibility = PACKAGE)
80 public class EthernetTracker {
81     private static final int INTERFACE_MODE_CLIENT = 1;
82     private static final int INTERFACE_MODE_SERVER = 2;
83 
84     private static final String TAG = EthernetTracker.class.getSimpleName();
85     private static final boolean DBG = EthernetNetworkFactory.DBG;
86 
87     private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+";
88 
89     /**
90      * Interface names we track. This is a product-dependent regular expression, plus,
91      * if setIncludeTestInterfaces is true, any test interfaces.
92      */
93     private volatile String mIfaceMatch;
94 
95     /**
96      * Track test interfaces if true, don't track otherwise.
97      */
98     private boolean mIncludeTestInterfaces = false;
99 
100     /** Mapping between {iface name | mac address} -> {NetworkCapabilities} */
101     private final ConcurrentHashMap<String, NetworkCapabilities> mNetworkCapabilities =
102             new ConcurrentHashMap<>();
103     private final ConcurrentHashMap<String, IpConfiguration> mIpConfigurations =
104             new ConcurrentHashMap<>();
105 
106     private final Context mContext;
107     private final INetd mNetd;
108     private final Handler mHandler;
109     private final EthernetNetworkFactory mFactory;
110     private final EthernetConfigStore mConfigStore;
111     private final Dependencies mDeps;
112 
113     private final RemoteCallbackList<IEthernetServiceListener> mListeners =
114             new RemoteCallbackList<>();
115     private final TetheredInterfaceRequestList mTetheredInterfaceRequests =
116             new TetheredInterfaceRequestList();
117 
118     // Used only on the handler thread
119     private String mDefaultInterface;
120     private int mDefaultInterfaceMode = INTERFACE_MODE_CLIENT;
121     // Tracks whether clients were notified that the tethered interface is available
122     private boolean mTetheredInterfaceWasAvailable = false;
123     private volatile IpConfiguration mIpConfigForDefaultInterface;
124 
125     private int mEthernetState = ETHERNET_STATE_ENABLED;
126 
127     private class TetheredInterfaceRequestList extends
128             RemoteCallbackList<ITetheredInterfaceCallback> {
129         @Override
onCallbackDied(ITetheredInterfaceCallback cb, Object cookie)130         public void onCallbackDied(ITetheredInterfaceCallback cb, Object cookie) {
131             mHandler.post(EthernetTracker.this::maybeUntetherDefaultInterface);
132         }
133     }
134 
135     public static class Dependencies {
getInterfaceRegexFromResource(Context context)136         public String getInterfaceRegexFromResource(Context context) {
137             final ConnectivityResources resources = new ConnectivityResources(context);
138             return resources.get().getString(
139                     com.android.connectivity.resources.R.string.config_ethernet_iface_regex);
140         }
141 
getInterfaceConfigFromResource(Context context)142         public String[] getInterfaceConfigFromResource(Context context) {
143             final ConnectivityResources resources = new ConnectivityResources(context);
144             return resources.get().getStringArray(
145                     com.android.connectivity.resources.R.array.config_ethernet_interfaces);
146         }
147     }
148 
EthernetTracker(@onNull final Context context, @NonNull final Handler handler, @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd)149     EthernetTracker(@NonNull final Context context, @NonNull final Handler handler,
150             @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd) {
151         this(context, handler, factory, netd, new Dependencies());
152     }
153 
154     @VisibleForTesting
EthernetTracker(@onNull final Context context, @NonNull final Handler handler, @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd, @NonNull final Dependencies deps)155     EthernetTracker(@NonNull final Context context, @NonNull final Handler handler,
156             @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd,
157             @NonNull final Dependencies deps) {
158         mContext = context;
159         mHandler = handler;
160         mFactory = factory;
161         mNetd = netd;
162         mDeps = deps;
163 
164         // Interface match regex.
165         updateIfaceMatchRegexp();
166 
167         // Read default Ethernet interface configuration from resources
168         final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context);
169         for (String strConfig : interfaceConfigs) {
170             parseEthernetConfig(strConfig);
171         }
172 
173         mConfigStore = new EthernetConfigStore();
174     }
175 
start()176     void start() {
177         mFactory.register();
178         mConfigStore.read();
179 
180         // Default interface is just the first one we want to track.
181         mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface();
182         final ArrayMap<String, IpConfiguration> configs = mConfigStore.getIpConfigurations();
183         for (int i = 0; i < configs.size(); i++) {
184             mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i));
185         }
186 
187         try {
188             PermissionUtils.enforceNetworkStackPermission(mContext);
189             mNetd.registerUnsolicitedEventListener(new InterfaceObserver());
190         } catch (RemoteException | ServiceSpecificException e) {
191             Log.e(TAG, "Could not register InterfaceObserver " + e);
192         }
193 
194         mHandler.post(this::trackAvailableInterfaces);
195     }
196 
updateIpConfiguration(String iface, IpConfiguration ipConfiguration)197     void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) {
198         if (DBG) {
199             Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration);
200         }
201         writeIpConfiguration(iface, ipConfiguration);
202         mHandler.post(() -> {
203             mFactory.updateInterface(iface, ipConfiguration, null, null);
204             broadcastInterfaceStateChange(iface);
205         });
206     }
207 
writeIpConfiguration(@onNull final String iface, @NonNull final IpConfiguration ipConfig)208     private void writeIpConfiguration(@NonNull final String iface,
209             @NonNull final IpConfiguration ipConfig) {
210         mConfigStore.write(iface, ipConfig);
211         mIpConfigurations.put(iface, ipConfig);
212     }
213 
getIpConfigurationForCallback(String iface, int state)214     private IpConfiguration getIpConfigurationForCallback(String iface, int state) {
215         return (state == EthernetManager.STATE_ABSENT) ? null : getOrCreateIpConfiguration(iface);
216     }
217 
ensureRunningOnEthernetServiceThread()218     private void ensureRunningOnEthernetServiceThread() {
219         if (mHandler.getLooper().getThread() != Thread.currentThread()) {
220             throw new IllegalStateException(
221                     "Not running on EthernetService thread: "
222                             + Thread.currentThread().getName());
223         }
224     }
225 
226     /**
227      * Broadcast the link state or IpConfiguration change of existing Ethernet interfaces to all
228      * listeners.
229      */
broadcastInterfaceStateChange(@onNull String iface)230     protected void broadcastInterfaceStateChange(@NonNull String iface) {
231         ensureRunningOnEthernetServiceThread();
232         final int state = getInterfaceState(iface);
233         final int role = getInterfaceRole(iface);
234         final IpConfiguration config = getIpConfigurationForCallback(iface, state);
235         final int n = mListeners.beginBroadcast();
236         for (int i = 0; i < n; i++) {
237             try {
238                 mListeners.getBroadcastItem(i).onInterfaceStateChanged(iface, state, role, config);
239             } catch (RemoteException e) {
240                 // Do nothing here.
241             }
242         }
243         mListeners.finishBroadcast();
244     }
245 
246     /**
247      * Unicast the interface state or IpConfiguration change of existing Ethernet interfaces to a
248      * specific listener.
249      */
unicastInterfaceStateChange(@onNull IEthernetServiceListener listener, @NonNull String iface)250     protected void unicastInterfaceStateChange(@NonNull IEthernetServiceListener listener,
251             @NonNull String iface) {
252         ensureRunningOnEthernetServiceThread();
253         final int state = mFactory.getInterfaceState(iface);
254         final int role = getInterfaceRole(iface);
255         final IpConfiguration config = getIpConfigurationForCallback(iface, state);
256         try {
257             listener.onInterfaceStateChanged(iface, state, role, config);
258         } catch (RemoteException e) {
259             // Do nothing here.
260         }
261     }
262 
263     @VisibleForTesting(visibility = PACKAGE)
updateConfiguration(@onNull final String iface, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, @Nullable final INetworkInterfaceOutcomeReceiver listener)264     protected void updateConfiguration(@NonNull final String iface,
265             @Nullable final IpConfiguration ipConfig,
266             @Nullable final NetworkCapabilities capabilities,
267             @Nullable final INetworkInterfaceOutcomeReceiver listener) {
268         if (DBG) {
269             Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities
270                     + ", ipConfig: " + ipConfig);
271         }
272 
273         final IpConfiguration localIpConfig = ipConfig == null
274                 ? null : new IpConfiguration(ipConfig);
275         if (ipConfig != null) {
276             writeIpConfiguration(iface, localIpConfig);
277         }
278 
279         if (null != capabilities) {
280             mNetworkCapabilities.put(iface, capabilities);
281         }
282         mHandler.post(() -> {
283             mFactory.updateInterface(iface, localIpConfig, capabilities, listener);
284             broadcastInterfaceStateChange(iface);
285         });
286     }
287 
288     @VisibleForTesting(visibility = PACKAGE)
connectNetwork(@onNull final String iface, @Nullable final INetworkInterfaceOutcomeReceiver listener)289     protected void connectNetwork(@NonNull final String iface,
290             @Nullable final INetworkInterfaceOutcomeReceiver listener) {
291         mHandler.post(() -> updateInterfaceState(iface, true, listener));
292     }
293 
294     @VisibleForTesting(visibility = PACKAGE)
disconnectNetwork(@onNull final String iface, @Nullable final INetworkInterfaceOutcomeReceiver listener)295     protected void disconnectNetwork(@NonNull final String iface,
296             @Nullable final INetworkInterfaceOutcomeReceiver listener) {
297         mHandler.post(() -> updateInterfaceState(iface, false, listener));
298     }
299 
getIpConfiguration(String iface)300     IpConfiguration getIpConfiguration(String iface) {
301         return mIpConfigurations.get(iface);
302     }
303 
304     @VisibleForTesting(visibility = PACKAGE)
isTrackingInterface(String iface)305     protected boolean isTrackingInterface(String iface) {
306         return mFactory.hasInterface(iface);
307     }
308 
getInterfaces(boolean includeRestricted)309     String[] getInterfaces(boolean includeRestricted) {
310         return mFactory.getAvailableInterfaces(includeRestricted);
311     }
312 
getInterfaceList()313     List<String> getInterfaceList() {
314         final List<String> interfaceList = new ArrayList<String>();
315         final String[] ifaces;
316         try {
317             ifaces = mNetd.interfaceGetList();
318         } catch (RemoteException e) {
319             Log.e(TAG, "Could not get list of interfaces " + e);
320             return interfaceList;
321         }
322         final String ifaceMatch = mIfaceMatch;
323         for (String iface : ifaces) {
324             if (iface.matches(ifaceMatch)) interfaceList.add(iface);
325         }
326         return interfaceList;
327     }
328 
329     /**
330      * Returns true if given interface was configured as restricted (doesn't have
331      * NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false.
332      */
isRestrictedInterface(String iface)333     boolean isRestrictedInterface(String iface) {
334         final NetworkCapabilities nc = mNetworkCapabilities.get(iface);
335         return nc != null && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
336     }
337 
addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks)338     void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) {
339         mHandler.post(() -> {
340             if (!mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks))) {
341                 // Remote process has already died
342                 return;
343             }
344             for (String iface : getInterfaces(canUseRestrictedNetworks)) {
345                 unicastInterfaceStateChange(listener, iface);
346             }
347 
348             unicastEthernetStateChange(listener, mEthernetState);
349         });
350     }
351 
removeListener(IEthernetServiceListener listener)352     void removeListener(IEthernetServiceListener listener) {
353         mHandler.post(() -> mListeners.unregister(listener));
354     }
355 
setIncludeTestInterfaces(boolean include)356     public void setIncludeTestInterfaces(boolean include) {
357         mHandler.post(() -> {
358             mIncludeTestInterfaces = include;
359             updateIfaceMatchRegexp();
360             if (!include) {
361                 removeTestData();
362             }
363             mHandler.post(() -> trackAvailableInterfaces());
364         });
365     }
366 
removeTestData()367     private void removeTestData() {
368         removeTestIpData();
369         removeTestCapabilityData();
370     }
371 
removeTestIpData()372     private void removeTestIpData() {
373         final Iterator<String> iterator = mIpConfigurations.keySet().iterator();
374         while (iterator.hasNext()) {
375             final String iface = iterator.next();
376             if (iface.matches(TEST_IFACE_REGEXP)) {
377                 mConfigStore.write(iface, null);
378                 iterator.remove();
379             }
380         }
381     }
382 
removeTestCapabilityData()383     private void removeTestCapabilityData() {
384         mNetworkCapabilities.keySet().removeIf(iface -> iface.matches(TEST_IFACE_REGEXP));
385     }
386 
requestTetheredInterface(ITetheredInterfaceCallback callback)387     public void requestTetheredInterface(ITetheredInterfaceCallback callback) {
388         mHandler.post(() -> {
389             if (!mTetheredInterfaceRequests.register(callback)) {
390                 // Remote process has already died
391                 return;
392             }
393             if (mDefaultInterfaceMode == INTERFACE_MODE_SERVER) {
394                 if (mTetheredInterfaceWasAvailable) {
395                     notifyTetheredInterfaceAvailable(callback, mDefaultInterface);
396                 }
397                 return;
398             }
399 
400             setDefaultInterfaceMode(INTERFACE_MODE_SERVER);
401         });
402     }
403 
releaseTetheredInterface(ITetheredInterfaceCallback callback)404     public void releaseTetheredInterface(ITetheredInterfaceCallback callback) {
405         mHandler.post(() -> {
406             mTetheredInterfaceRequests.unregister(callback);
407             maybeUntetherDefaultInterface();
408         });
409     }
410 
notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface)411     private void notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface) {
412         try {
413             cb.onAvailable(iface);
414         } catch (RemoteException e) {
415             Log.e(TAG, "Error sending tethered interface available callback", e);
416         }
417     }
418 
notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb)419     private void notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb) {
420         try {
421             cb.onUnavailable();
422         } catch (RemoteException e) {
423             Log.e(TAG, "Error sending tethered interface available callback", e);
424         }
425     }
426 
maybeUntetherDefaultInterface()427     private void maybeUntetherDefaultInterface() {
428         if (mTetheredInterfaceRequests.getRegisteredCallbackCount() > 0) return;
429         if (mDefaultInterfaceMode == INTERFACE_MODE_CLIENT) return;
430         setDefaultInterfaceMode(INTERFACE_MODE_CLIENT);
431     }
432 
setDefaultInterfaceMode(int mode)433     private void setDefaultInterfaceMode(int mode) {
434         Log.d(TAG, "Setting default interface mode to " + mode);
435         mDefaultInterfaceMode = mode;
436         if (mDefaultInterface != null) {
437             removeInterface(mDefaultInterface);
438             addInterface(mDefaultInterface);
439             // when this broadcast is sent, any calls to notifyTetheredInterfaceAvailable or
440             // notifyTetheredInterfaceUnavailable have already happened
441             broadcastInterfaceStateChange(mDefaultInterface);
442         }
443     }
444 
getInterfaceState(final String iface)445     private int getInterfaceState(final String iface) {
446         if (mFactory.hasInterface(iface)) {
447             return mFactory.getInterfaceState(iface);
448         }
449         if (getInterfaceMode(iface) == INTERFACE_MODE_SERVER) {
450             // server mode interfaces are not tracked by the factory.
451             // TODO(b/234743836): interface state for server mode interfaces is not tracked
452             // properly; just return link up.
453             return EthernetManager.STATE_LINK_UP;
454         }
455         return EthernetManager.STATE_ABSENT;
456     }
457 
getInterfaceRole(final String iface)458     private int getInterfaceRole(final String iface) {
459         if (mFactory.hasInterface(iface)) {
460             // only client mode interfaces are tracked by the factory.
461             return EthernetManager.ROLE_CLIENT;
462         }
463         if (getInterfaceMode(iface) == INTERFACE_MODE_SERVER) {
464             return EthernetManager.ROLE_SERVER;
465         }
466         return EthernetManager.ROLE_NONE;
467     }
468 
getInterfaceMode(final String iface)469     private int getInterfaceMode(final String iface) {
470         if (iface.equals(mDefaultInterface)) {
471             return mDefaultInterfaceMode;
472         }
473         return INTERFACE_MODE_CLIENT;
474     }
475 
removeInterface(String iface)476     private void removeInterface(String iface) {
477         mFactory.removeInterface(iface);
478         maybeUpdateServerModeInterfaceState(iface, false);
479     }
480 
stopTrackingInterface(String iface)481     private void stopTrackingInterface(String iface) {
482         removeInterface(iface);
483         if (iface.equals(mDefaultInterface)) {
484             mDefaultInterface = null;
485         }
486         broadcastInterfaceStateChange(iface);
487     }
488 
addInterface(String iface)489     private void addInterface(String iface) {
490         InterfaceConfigurationParcel config = null;
491         // Bring up the interface so we get link status indications.
492         try {
493             PermissionUtils.enforceNetworkStackPermission(mContext);
494             NetdUtils.setInterfaceUp(mNetd, iface);
495             config = NetdUtils.getInterfaceConfigParcel(mNetd, iface);
496         } catch (IllegalStateException e) {
497             // Either the system is crashing or the interface has disappeared. Just ignore the
498             // error; we haven't modified any state because we only do that if our calls succeed.
499             Log.e(TAG, "Error upping interface " + iface, e);
500         }
501 
502         if (config == null) {
503             Log.e(TAG, "Null interface config parcelable for " + iface + ". Bailing out.");
504             return;
505         }
506 
507         final String hwAddress = config.hwAddr;
508 
509         NetworkCapabilities nc = mNetworkCapabilities.get(iface);
510         if (nc == null) {
511             // Try to resolve using mac address
512             nc = mNetworkCapabilities.get(hwAddress);
513             if (nc == null) {
514                 final boolean isTestIface = iface.matches(TEST_IFACE_REGEXP);
515                 nc = createDefaultNetworkCapabilities(isTestIface);
516             }
517         }
518 
519         final int mode = getInterfaceMode(iface);
520         if (mode == INTERFACE_MODE_CLIENT) {
521             IpConfiguration ipConfiguration = getOrCreateIpConfiguration(iface);
522             Log.d(TAG, "Tracking interface in client mode: " + iface);
523             mFactory.addInterface(iface, hwAddress, ipConfiguration, nc);
524         } else {
525             maybeUpdateServerModeInterfaceState(iface, true);
526         }
527 
528         // Note: if the interface already has link (e.g., if we crashed and got
529         // restarted while it was running), we need to fake a link up notification so we
530         // start configuring it.
531         if (NetdUtils.hasFlag(config, "running")) {
532             updateInterfaceState(iface, true);
533         }
534     }
535 
updateInterfaceState(String iface, boolean up)536     private void updateInterfaceState(String iface, boolean up) {
537         updateInterfaceState(iface, up, null /* listener */);
538     }
539 
updateInterfaceState(@onNull final String iface, final boolean up, @Nullable final INetworkInterfaceOutcomeReceiver listener)540     private void updateInterfaceState(@NonNull final String iface, final boolean up,
541             @Nullable final INetworkInterfaceOutcomeReceiver listener) {
542         final int mode = getInterfaceMode(iface);
543         final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT)
544                 && mFactory.updateInterfaceLinkState(iface, up, listener);
545 
546         if (factoryLinkStateUpdated) {
547             broadcastInterfaceStateChange(iface);
548         }
549     }
550 
maybeUpdateServerModeInterfaceState(String iface, boolean available)551     private void maybeUpdateServerModeInterfaceState(String iface, boolean available) {
552         if (available == mTetheredInterfaceWasAvailable || !iface.equals(mDefaultInterface)) return;
553 
554         Log.d(TAG, (available ? "Tracking" : "No longer tracking")
555                 + " interface in server mode: " + iface);
556 
557         final int pendingCbs = mTetheredInterfaceRequests.beginBroadcast();
558         for (int i = 0; i < pendingCbs; i++) {
559             ITetheredInterfaceCallback item = mTetheredInterfaceRequests.getBroadcastItem(i);
560             if (available) {
561                 notifyTetheredInterfaceAvailable(item, iface);
562             } else {
563                 notifyTetheredInterfaceUnavailable(item);
564             }
565         }
566         mTetheredInterfaceRequests.finishBroadcast();
567         mTetheredInterfaceWasAvailable = available;
568     }
569 
maybeTrackInterface(String iface)570     private void maybeTrackInterface(String iface) {
571         if (!iface.matches(mIfaceMatch)) {
572             return;
573         }
574 
575         // If we don't already track this interface, and if this interface matches
576         // our regex, start tracking it.
577         if (mFactory.hasInterface(iface) || iface.equals(mDefaultInterface)) {
578             if (DBG) Log.w(TAG, "Ignoring already-tracked interface " + iface);
579             return;
580         }
581         if (DBG) Log.i(TAG, "maybeTrackInterface: " + iface);
582 
583         // TODO: avoid making an interface default if it has configured NetworkCapabilities.
584         if (mDefaultInterface == null) {
585             mDefaultInterface = iface;
586         }
587 
588         if (mIpConfigForDefaultInterface != null) {
589             updateIpConfiguration(iface, mIpConfigForDefaultInterface);
590             mIpConfigForDefaultInterface = null;
591         }
592 
593         addInterface(iface);
594 
595         broadcastInterfaceStateChange(iface);
596     }
597 
trackAvailableInterfaces()598     private void trackAvailableInterfaces() {
599         try {
600             final String[] ifaces = mNetd.interfaceGetList();
601             for (String iface : ifaces) {
602                 maybeTrackInterface(iface);
603             }
604         } catch (RemoteException | ServiceSpecificException e) {
605             Log.e(TAG, "Could not get list of interfaces " + e);
606         }
607     }
608 
609     @VisibleForTesting
610     class InterfaceObserver extends BaseNetdUnsolicitedEventListener {
611 
612         @Override
onInterfaceLinkStateChanged(String iface, boolean up)613         public void onInterfaceLinkStateChanged(String iface, boolean up) {
614             if (DBG) {
615                 Log.i(TAG, "interfaceLinkStateChanged, iface: " + iface + ", up: " + up);
616             }
617             mHandler.post(() -> {
618                 if (mEthernetState == ETHERNET_STATE_DISABLED) return;
619                 updateInterfaceState(iface, up);
620             });
621         }
622 
623         @Override
onInterfaceAdded(String iface)624         public void onInterfaceAdded(String iface) {
625             if (DBG) {
626                 Log.i(TAG, "onInterfaceAdded, iface: " + iface);
627             }
628             mHandler.post(() -> {
629                 if (mEthernetState == ETHERNET_STATE_DISABLED) return;
630                 maybeTrackInterface(iface);
631             });
632         }
633 
634         @Override
onInterfaceRemoved(String iface)635         public void onInterfaceRemoved(String iface) {
636             if (DBG) {
637                 Log.i(TAG, "onInterfaceRemoved, iface: " + iface);
638             }
639             mHandler.post(() -> {
640                 if (mEthernetState == ETHERNET_STATE_DISABLED) return;
641                 stopTrackingInterface(iface);
642             });
643         }
644     }
645 
646     private static class ListenerInfo {
647 
648         boolean canUseRestrictedNetworks = false;
649 
ListenerInfo(boolean canUseRestrictedNetworks)650         ListenerInfo(boolean canUseRestrictedNetworks) {
651             this.canUseRestrictedNetworks = canUseRestrictedNetworks;
652         }
653     }
654 
655     /**
656      * Parses an Ethernet interface configuration
657      *
658      * @param configString represents an Ethernet configuration in the following format: {@code
659      * <interface name|mac address>;[Network Capabilities];[IP config];[Override Transport]}
660      */
parseEthernetConfig(String configString)661     private void parseEthernetConfig(String configString) {
662         final EthernetTrackerConfig config = createEthernetTrackerConfig(configString);
663         NetworkCapabilities nc = createNetworkCapabilities(
664                 !TextUtils.isEmpty(config.mCapabilities)  /* clear default capabilities */,
665                 config.mCapabilities, config.mTransport).build();
666         mNetworkCapabilities.put(config.mIface, nc);
667 
668         if (null != config.mIpConfig) {
669             IpConfiguration ipConfig = parseStaticIpConfiguration(config.mIpConfig);
670             mIpConfigurations.put(config.mIface, ipConfig);
671         }
672     }
673 
674     @VisibleForTesting
createEthernetTrackerConfig(@onNull final String configString)675     static EthernetTrackerConfig createEthernetTrackerConfig(@NonNull final String configString) {
676         Objects.requireNonNull(configString, "EthernetTrackerConfig requires non-null config");
677         return new EthernetTrackerConfig(configString.split(";", /* limit of tokens */ 4));
678     }
679 
createDefaultNetworkCapabilities(boolean isTestIface)680     private static NetworkCapabilities createDefaultNetworkCapabilities(boolean isTestIface) {
681         NetworkCapabilities.Builder builder = createNetworkCapabilities(
682                 false /* clear default capabilities */, null, null)
683                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
684                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
685                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
686                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED)
687                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
688                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
689 
690         if (isTestIface) {
691             builder.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
692         } else {
693             builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
694         }
695 
696         return builder.build();
697     }
698 
699     /**
700      * Parses a static list of network capabilities
701      *
702      * @param clearDefaultCapabilities Indicates whether or not to clear any default capabilities
703      * @param commaSeparatedCapabilities A comma separated string list of integer encoded
704      *                                   NetworkCapability.NET_CAPABILITY_* values
705      * @param overrideTransport A string representing a single integer encoded override transport
706      *                          type. Must be one of the NetworkCapability.TRANSPORT_*
707      *                          values. TRANSPORT_VPN is not supported. Errors with input
708      *                          will cause the override to be ignored.
709      */
710     @VisibleForTesting
createNetworkCapabilities( boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities, @Nullable String overrideTransport)711     static NetworkCapabilities.Builder createNetworkCapabilities(
712             boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities,
713             @Nullable String overrideTransport) {
714 
715         final NetworkCapabilities.Builder builder = clearDefaultCapabilities
716                 ? NetworkCapabilities.Builder.withoutDefaultCapabilities()
717                 : new NetworkCapabilities.Builder();
718 
719         // Determine the transport type. If someone has tried to define an override transport then
720         // attempt to add it. Since we can only have one override, all errors with it will
721         // gracefully default back to TRANSPORT_ETHERNET and warn the user. VPN is not allowed as an
722         // override type. Wifi Aware and LoWPAN are currently unsupported as well.
723         int transport = NetworkCapabilities.TRANSPORT_ETHERNET;
724         if (!TextUtils.isEmpty(overrideTransport)) {
725             try {
726                 int parsedTransport = Integer.valueOf(overrideTransport);
727                 if (parsedTransport == NetworkCapabilities.TRANSPORT_VPN
728                         || parsedTransport == NetworkCapabilities.TRANSPORT_WIFI_AWARE
729                         || parsedTransport == NetworkCapabilities.TRANSPORT_LOWPAN) {
730                     Log.e(TAG, "Override transport '" + parsedTransport + "' is not supported. "
731                             + "Defaulting to TRANSPORT_ETHERNET");
732                 } else {
733                     transport = parsedTransport;
734                 }
735             } catch (NumberFormatException nfe) {
736                 Log.e(TAG, "Override transport type '" + overrideTransport + "' "
737                         + "could not be parsed. Defaulting to TRANSPORT_ETHERNET");
738             }
739         }
740 
741         // Apply the transport. If the user supplied a valid number that is not a valid transport
742         // then adding will throw an exception. Default back to TRANSPORT_ETHERNET if that happens
743         try {
744             builder.addTransportType(transport);
745         } catch (IllegalArgumentException iae) {
746             Log.e(TAG, transport + " is not a valid NetworkCapability.TRANSPORT_* value. "
747                     + "Defaulting to TRANSPORT_ETHERNET");
748             builder.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET);
749         }
750 
751         builder.setLinkUpstreamBandwidthKbps(100 * 1000);
752         builder.setLinkDownstreamBandwidthKbps(100 * 1000);
753 
754         if (!TextUtils.isEmpty(commaSeparatedCapabilities)) {
755             for (String strNetworkCapability : commaSeparatedCapabilities.split(",")) {
756                 if (!TextUtils.isEmpty(strNetworkCapability)) {
757                     try {
758                         builder.addCapability(Integer.valueOf(strNetworkCapability));
759                     } catch (NumberFormatException nfe) {
760                         Log.e(TAG, "Capability '" + strNetworkCapability + "' could not be parsed");
761                     } catch (IllegalArgumentException iae) {
762                         Log.e(TAG, strNetworkCapability + " is not a valid "
763                                 + "NetworkCapability.NET_CAPABILITY_* value");
764                     }
765                 }
766             }
767         }
768         // Ethernet networks have no way to update the following capabilities, so they always
769         // have them.
770         builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
771         builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
772         builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
773 
774         return builder;
775     }
776 
777     /**
778      * Parses static IP configuration.
779      *
780      * @param staticIpConfig represents static IP configuration in the following format: {@code
781      * ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
782      *     domains=<comma-sep-domains>}
783      */
784     @VisibleForTesting
parseStaticIpConfiguration(String staticIpConfig)785     static IpConfiguration parseStaticIpConfiguration(String staticIpConfig) {
786         final StaticIpConfiguration.Builder staticIpConfigBuilder =
787                 new StaticIpConfiguration.Builder();
788 
789         for (String keyValueAsString : staticIpConfig.trim().split(" ")) {
790             if (TextUtils.isEmpty(keyValueAsString)) continue;
791 
792             String[] pair = keyValueAsString.split("=");
793             if (pair.length != 2) {
794                 throw new IllegalArgumentException("Unexpected token: " + keyValueAsString
795                         + " in " + staticIpConfig);
796             }
797 
798             String key = pair[0];
799             String value = pair[1];
800 
801             switch (key) {
802                 case "ip":
803                     staticIpConfigBuilder.setIpAddress(new LinkAddress(value));
804                     break;
805                 case "domains":
806                     staticIpConfigBuilder.setDomains(value);
807                     break;
808                 case "gateway":
809                     staticIpConfigBuilder.setGateway(InetAddress.parseNumericAddress(value));
810                     break;
811                 case "dns": {
812                     ArrayList<InetAddress> dnsAddresses = new ArrayList<>();
813                     for (String address: value.split(",")) {
814                         dnsAddresses.add(InetAddress.parseNumericAddress(address));
815                     }
816                     staticIpConfigBuilder.setDnsServers(dnsAddresses);
817                     break;
818                 }
819                 default : {
820                     throw new IllegalArgumentException("Unexpected key: " + key
821                             + " in " + staticIpConfig);
822                 }
823             }
824         }
825         return createIpConfiguration(staticIpConfigBuilder.build());
826     }
827 
createIpConfiguration( @onNull final StaticIpConfiguration staticIpConfig)828     private static IpConfiguration createIpConfiguration(
829             @NonNull final StaticIpConfiguration staticIpConfig) {
830         return new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build();
831     }
832 
getOrCreateIpConfiguration(String iface)833     private IpConfiguration getOrCreateIpConfiguration(String iface) {
834         IpConfiguration ret = mIpConfigurations.get(iface);
835         if (ret != null) return ret;
836         ret = new IpConfiguration();
837         ret.setIpAssignment(IpAssignment.DHCP);
838         ret.setProxySettings(ProxySettings.NONE);
839         return ret;
840     }
841 
updateIfaceMatchRegexp()842     private void updateIfaceMatchRegexp() {
843         final String match = mDeps.getInterfaceRegexFromResource(mContext);
844         mIfaceMatch = mIncludeTestInterfaces
845                 ? "(" + match + "|" + TEST_IFACE_REGEXP + ")"
846                 : match;
847         Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'");
848     }
849 
850     /**
851      * Validate if a given interface is valid for testing.
852      *
853      * @param iface the name of the interface to validate.
854      * @return {@code true} if test interfaces are enabled and the given {@code iface} has a test
855      * interface prefix, {@code false} otherwise.
856      */
isValidTestInterface(@onNull final String iface)857     public boolean isValidTestInterface(@NonNull final String iface) {
858         return mIncludeTestInterfaces && iface.matches(TEST_IFACE_REGEXP);
859     }
860 
postAndWaitForRunnable(Runnable r)861     private void postAndWaitForRunnable(Runnable r) {
862         final ConditionVariable cv = new ConditionVariable();
863         if (mHandler.post(() -> {
864             r.run();
865             cv.open();
866         })) {
867             cv.block(2000L);
868         }
869     }
870 
871     @VisibleForTesting(visibility = PACKAGE)
setEthernetEnabled(boolean enabled)872     protected void setEthernetEnabled(boolean enabled) {
873         mHandler.post(() -> {
874             int newState = enabled ? ETHERNET_STATE_ENABLED : ETHERNET_STATE_DISABLED;
875             if (mEthernetState == newState) return;
876 
877             mEthernetState = newState;
878 
879             if (enabled) {
880                 trackAvailableInterfaces();
881             } else {
882                 // TODO: maybe also disable server mode interface as well.
883                 untrackFactoryInterfaces();
884             }
885             broadcastEthernetStateChange(mEthernetState);
886         });
887     }
888 
untrackFactoryInterfaces()889     private void untrackFactoryInterfaces() {
890         for (String iface : mFactory.getAvailableInterfaces(true /* includeRestricted */)) {
891             stopTrackingInterface(iface);
892         }
893     }
894 
unicastEthernetStateChange(@onNull IEthernetServiceListener listener, int state)895     private void unicastEthernetStateChange(@NonNull IEthernetServiceListener listener,
896             int state) {
897         ensureRunningOnEthernetServiceThread();
898         try {
899             listener.onEthernetStateChanged(state);
900         } catch (RemoteException e) {
901             // Do nothing here.
902         }
903     }
904 
broadcastEthernetStateChange(int state)905     private void broadcastEthernetStateChange(int state) {
906         ensureRunningOnEthernetServiceThread();
907         final int n = mListeners.beginBroadcast();
908         for (int i = 0; i < n; i++) {
909             try {
910                 mListeners.getBroadcastItem(i).onEthernetStateChanged(state);
911             } catch (RemoteException e) {
912                 // Do nothing here.
913             }
914         }
915         mListeners.finishBroadcast();
916     }
917 
dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args)918     void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
919         postAndWaitForRunnable(() -> {
920             pw.println(getClass().getSimpleName());
921             pw.println("Ethernet State: "
922                     + (mEthernetState == ETHERNET_STATE_ENABLED ? "enabled" : "disabled"));
923             pw.println("Ethernet interface name filter: " + mIfaceMatch);
924             pw.println("Default interface: " + mDefaultInterface);
925             pw.println("Default interface mode: " + mDefaultInterfaceMode);
926             pw.println("Tethered interface requests: "
927                     + mTetheredInterfaceRequests.getRegisteredCallbackCount());
928             pw.println("Listeners: " + mListeners.getRegisteredCallbackCount());
929             pw.println("IP Configurations:");
930             pw.increaseIndent();
931             for (String iface : mIpConfigurations.keySet()) {
932                 pw.println(iface + ": " + mIpConfigurations.get(iface));
933             }
934             pw.decreaseIndent();
935             pw.println();
936 
937             pw.println("Network Capabilities:");
938             pw.increaseIndent();
939             for (String iface : mNetworkCapabilities.keySet()) {
940                 pw.println(iface + ": " + mNetworkCapabilities.get(iface));
941             }
942             pw.decreaseIndent();
943             pw.println();
944 
945             mFactory.dump(fd, pw, args);
946         });
947     }
948 
949     @VisibleForTesting
950     static class EthernetTrackerConfig {
951         final String mIface;
952         final String mCapabilities;
953         final String mIpConfig;
954         final String mTransport;
955 
EthernetTrackerConfig(@onNull final String[] tokens)956         EthernetTrackerConfig(@NonNull final String[] tokens) {
957             Objects.requireNonNull(tokens, "EthernetTrackerConfig requires non-null tokens");
958             mIface = tokens[0];
959             mCapabilities = tokens.length > 1 ? tokens[1] : null;
960             mIpConfig = tokens.length > 2 && !TextUtils.isEmpty(tokens[2]) ? tokens[2] : null;
961             mTransport = tokens.length > 3 ? tokens[3] : null;
962         }
963     }
964 }
965