1 /*
2  * Copyright (C) 2010 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 android.net;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.text.TextUtils;
24 
25 import java.net.Inet4Address;
26 import java.net.Inet6Address;
27 import java.net.InetAddress;
28 import java.net.UnknownHostException;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Hashtable;
33 import java.util.List;
34 import java.util.Objects;
35 
36 /**
37  * Describes the properties of a network link.
38  *
39  * A link represents a connection to a network.
40  * It may have multiple addresses and multiple gateways,
41  * multiple dns servers but only one http proxy and one
42  * network interface.
43  *
44  * Note that this is just a holder of data.  Modifying it
45  * does not affect live networks.
46  *
47  */
48 public final class LinkProperties implements Parcelable {
49     // The interface described by the network link.
50     private String mIfaceName;
51     private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
52     private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
53     private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<InetAddress>();
54     private boolean mUsePrivateDns;
55     private String mPrivateDnsServerName;
56     private String mDomains;
57     private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
58     private ProxyInfo mHttpProxy;
59     private int mMtu;
60     // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
61     private String mTcpBufferSizes;
62 
63     private static final int MIN_MTU    = 68;
64     private static final int MIN_MTU_V6 = 1280;
65     private static final int MAX_MTU    = 10000;
66 
67     // Stores the properties of links that are "stacked" above this link.
68     // Indexed by interface name to allow modification and to prevent duplicates being added.
69     private Hashtable<String, LinkProperties> mStackedLinks =
70         new Hashtable<String, LinkProperties>();
71 
72     /**
73      * @hide
74      */
75     public static class CompareResult<T> {
76         public final List<T> removed = new ArrayList<T>();
77         public final List<T> added = new ArrayList<T>();
78 
CompareResult()79         public CompareResult() {}
80 
CompareResult(Collection<T> oldItems, Collection<T> newItems)81         public CompareResult(Collection<T> oldItems, Collection<T> newItems) {
82             if (oldItems != null) {
83                 removed.addAll(oldItems);
84             }
85             if (newItems != null) {
86                 for (T newItem : newItems) {
87                     if (!removed.remove(newItem)) {
88                         added.add(newItem);
89                     }
90                 }
91             }
92         }
93 
94         @Override
toString()95         public String toString() {
96             String retVal = "removed=[";
97             for (T addr : removed) retVal += addr.toString() + ",";
98             retVal += "] added=[";
99             for (T addr : added) retVal += addr.toString() + ",";
100             retVal += "]";
101             return retVal;
102         }
103     }
104 
105     /**
106      * @hide
107      */
108     public enum ProvisioningChange {
109         STILL_NOT_PROVISIONED,
110         LOST_PROVISIONING,
111         GAINED_PROVISIONING,
112         STILL_PROVISIONED,
113     }
114 
115     /**
116      * Compare the provisioning states of two LinkProperties instances.
117      *
118      * @hide
119      */
compareProvisioning( LinkProperties before, LinkProperties after)120     public static ProvisioningChange compareProvisioning(
121             LinkProperties before, LinkProperties after) {
122         if (before.isProvisioned() && after.isProvisioned()) {
123             // On dualstack networks, DHCPv4 renewals can occasionally fail.
124             // When this happens, IPv6-reachable services continue to function
125             // normally but IPv4-only services (naturally) fail.
126             //
127             // When an application using an IPv4-only service reports a bad
128             // network condition to the framework, attempts to re-validate
129             // the network succeed (since we support IPv6-only networks) and
130             // nothing is changed.
131             //
132             // For users, this is confusing and unexpected behaviour, and is
133             // not necessarily easy to diagnose.  Therefore, we treat changing
134             // from a dualstack network to an IPv6-only network equivalent to
135             // a total loss of provisioning.
136             //
137             // For one such example of this, see b/18867306.
138             //
139             // Additionally, losing IPv6 provisioning can result in TCP
140             // connections getting stuck until timeouts fire and other
141             // baffling failures. Therefore, loss of either IPv4 or IPv6 on a
142             // previously dualstack network is deemed a lost of provisioning.
143             if ((before.isIPv4Provisioned() && !after.isIPv4Provisioned()) ||
144                 (before.isIPv6Provisioned() && !after.isIPv6Provisioned())) {
145                 return ProvisioningChange.LOST_PROVISIONING;
146             }
147             return ProvisioningChange.STILL_PROVISIONED;
148         } else if (before.isProvisioned() && !after.isProvisioned()) {
149             return ProvisioningChange.LOST_PROVISIONING;
150         } else if (!before.isProvisioned() && after.isProvisioned()) {
151             return ProvisioningChange.GAINED_PROVISIONING;
152         } else {  // !before.isProvisioned() && !after.isProvisioned()
153             return ProvisioningChange.STILL_NOT_PROVISIONED;
154         }
155     }
156 
157     /**
158      * @hide
159      */
LinkProperties()160     public LinkProperties() {
161     }
162 
163     /**
164      * @hide
165      */
LinkProperties(LinkProperties source)166     public LinkProperties(LinkProperties source) {
167         if (source != null) {
168             mIfaceName = source.getInterfaceName();
169             for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
170             for (InetAddress i : source.getDnsServers()) mDnses.add(i);
171             for (InetAddress i : source.getValidatedPrivateDnsServers()) {
172                 mValidatedPrivateDnses.add(i);
173             }
174             mUsePrivateDns = source.mUsePrivateDns;
175             mPrivateDnsServerName = source.mPrivateDnsServerName;
176             mDomains = source.getDomains();
177             for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
178             mHttpProxy = (source.getHttpProxy() == null)  ?
179                     null : new ProxyInfo(source.getHttpProxy());
180             for (LinkProperties l: source.mStackedLinks.values()) {
181                 addStackedLink(l);
182             }
183             setMtu(source.getMtu());
184             mTcpBufferSizes = source.mTcpBufferSizes;
185         }
186     }
187 
188     /**
189      * Sets the interface name for this link.  All {@link RouteInfo} already set for this
190      * will have their interface changed to match this new value.
191      *
192      * @param iface The name of the network interface used for this link.
193      * @hide
194      */
setInterfaceName(String iface)195     public void setInterfaceName(String iface) {
196         mIfaceName = iface;
197         ArrayList<RouteInfo> newRoutes = new ArrayList<RouteInfo>(mRoutes.size());
198         for (RouteInfo route : mRoutes) {
199             newRoutes.add(routeWithInterface(route));
200         }
201         mRoutes = newRoutes;
202     }
203 
204     /**
205      * Gets the interface name for this link.  May be {@code null} if not set.
206      *
207      * @return The interface name set for this link or {@code null}.
208      */
getInterfaceName()209     public @Nullable String getInterfaceName() {
210         return mIfaceName;
211     }
212 
213     /**
214      * @hide
215      */
getAllInterfaceNames()216     public List<String> getAllInterfaceNames() {
217         List<String> interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
218         if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
219         for (LinkProperties stacked: mStackedLinks.values()) {
220             interfaceNames.addAll(stacked.getAllInterfaceNames());
221         }
222         return interfaceNames;
223     }
224 
225     /**
226      * Returns all the addresses on this link.  We often think of a link having a single address,
227      * however, particularly with Ipv6 several addresses are typical.  Note that the
228      * {@code LinkProperties} actually contains {@link LinkAddress} objects which also include
229      * prefix lengths for each address.  This is a simplified utility alternative to
230      * {@link LinkProperties#getLinkAddresses}.
231      *
232      * @return An umodifiable {@link List} of {@link InetAddress} for this link.
233      * @hide
234      */
getAddresses()235     public List<InetAddress> getAddresses() {
236         List<InetAddress> addresses = new ArrayList<InetAddress>();
237         for (LinkAddress linkAddress : mLinkAddresses) {
238             addresses.add(linkAddress.getAddress());
239         }
240         return Collections.unmodifiableList(addresses);
241     }
242 
243     /**
244      * Returns all the addresses on this link and all the links stacked above it.
245      * @hide
246      */
getAllAddresses()247     public List<InetAddress> getAllAddresses() {
248         List<InetAddress> addresses = new ArrayList<InetAddress>();
249         for (LinkAddress linkAddress : mLinkAddresses) {
250             addresses.add(linkAddress.getAddress());
251         }
252         for (LinkProperties stacked: mStackedLinks.values()) {
253             addresses.addAll(stacked.getAllAddresses());
254         }
255         return addresses;
256     }
257 
findLinkAddressIndex(LinkAddress address)258     private int findLinkAddressIndex(LinkAddress address) {
259         for (int i = 0; i < mLinkAddresses.size(); i++) {
260             if (mLinkAddresses.get(i).isSameAddressAs(address)) {
261                 return i;
262             }
263         }
264         return -1;
265     }
266 
267     /**
268      * Adds a {@link LinkAddress} to this {@code LinkProperties} if a {@link LinkAddress} of the
269      * same address/prefix does not already exist.  If it does exist it is replaced.
270      * @param address The {@code LinkAddress} to add.
271      * @return true if {@code address} was added or updated, false otherwise.
272      * @hide
273      */
addLinkAddress(LinkAddress address)274     public boolean addLinkAddress(LinkAddress address) {
275         if (address == null) {
276             return false;
277         }
278         int i = findLinkAddressIndex(address);
279         if (i < 0) {
280             // Address was not present. Add it.
281             mLinkAddresses.add(address);
282             return true;
283         } else if (mLinkAddresses.get(i).equals(address)) {
284             // Address was present and has same properties. Do nothing.
285             return false;
286         } else {
287             // Address was present and has different properties. Update it.
288             mLinkAddresses.set(i, address);
289             return true;
290         }
291     }
292 
293     /**
294      * Removes a {@link LinkAddress} from this {@code LinkProperties}.  Specifically, matches
295      * and {@link LinkAddress} with the same address and prefix.
296      *
297      * @param toRemove A {@link LinkAddress} specifying the address to remove.
298      * @return true if the address was removed, false if it did not exist.
299      * @hide
300      */
removeLinkAddress(LinkAddress toRemove)301     public boolean removeLinkAddress(LinkAddress toRemove) {
302         int i = findLinkAddressIndex(toRemove);
303         if (i >= 0) {
304             mLinkAddresses.remove(i);
305             return true;
306         }
307         return false;
308     }
309 
310     /**
311      * Returns all the {@link LinkAddress} on this link.  Typically a link will have
312      * one IPv4 address and one or more IPv6 addresses.
313      *
314      * @return An unmodifiable {@link List} of {@link LinkAddress} for this link.
315      */
getLinkAddresses()316     public List<LinkAddress> getLinkAddresses() {
317         return Collections.unmodifiableList(mLinkAddresses);
318     }
319 
320     /**
321      * Returns all the addresses on this link and all the links stacked above it.
322      * @hide
323      */
getAllLinkAddresses()324     public List<LinkAddress> getAllLinkAddresses() {
325         List<LinkAddress> addresses = new ArrayList<LinkAddress>();
326         addresses.addAll(mLinkAddresses);
327         for (LinkProperties stacked: mStackedLinks.values()) {
328             addresses.addAll(stacked.getAllLinkAddresses());
329         }
330         return addresses;
331     }
332 
333     /**
334      * Replaces the {@link LinkAddress} in this {@code LinkProperties} with
335      * the given {@link Collection} of {@link LinkAddress}.
336      *
337      * @param addresses The {@link Collection} of {@link LinkAddress} to set in this
338      *                  object.
339      * @hide
340      */
setLinkAddresses(Collection<LinkAddress> addresses)341     public void setLinkAddresses(Collection<LinkAddress> addresses) {
342         mLinkAddresses.clear();
343         for (LinkAddress address: addresses) {
344             addLinkAddress(address);
345         }
346     }
347 
348     /**
349      * Adds the given {@link InetAddress} to the list of DNS servers, if not present.
350      *
351      * @param dnsServer The {@link InetAddress} to add to the list of DNS servers.
352      * @return true if the DNS server was added, false if it was already present.
353      * @hide
354      */
addDnsServer(InetAddress dnsServer)355     public boolean addDnsServer(InetAddress dnsServer) {
356         if (dnsServer != null && !mDnses.contains(dnsServer)) {
357             mDnses.add(dnsServer);
358             return true;
359         }
360         return false;
361     }
362 
363     /**
364      * Removes the given {@link InetAddress} from the list of DNS servers.
365      *
366      * @param dnsServer The {@link InetAddress} to remove from the list of DNS servers.
367      * @return true if the DNS server was removed, false if it did not exist.
368      * @hide
369      */
removeDnsServer(InetAddress dnsServer)370     public boolean removeDnsServer(InetAddress dnsServer) {
371         if (dnsServer != null) {
372             return mDnses.remove(dnsServer);
373         }
374         return false;
375     }
376 
377     /**
378      * Replaces the DNS servers in this {@code LinkProperties} with
379      * the given {@link Collection} of {@link InetAddress} objects.
380      *
381      * @param dnsServers The {@link Collection} of DNS servers to set in this object.
382      * @hide
383      */
setDnsServers(Collection<InetAddress> dnsServers)384     public void setDnsServers(Collection<InetAddress> dnsServers) {
385         mDnses.clear();
386         for (InetAddress dnsServer: dnsServers) {
387             addDnsServer(dnsServer);
388         }
389     }
390 
391     /**
392      * Returns all the {@link InetAddress} for DNS servers on this link.
393      *
394      * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on
395      *         this link.
396      */
getDnsServers()397     public List<InetAddress> getDnsServers() {
398         return Collections.unmodifiableList(mDnses);
399     }
400 
401     /**
402      * Set whether private DNS is currently in use on this network.
403      *
404      * @param usePrivateDns The private DNS state.
405      * @hide
406      */
setUsePrivateDns(boolean usePrivateDns)407     public void setUsePrivateDns(boolean usePrivateDns) {
408         mUsePrivateDns = usePrivateDns;
409     }
410 
411     /**
412      * Returns whether private DNS is currently in use on this network. When
413      * private DNS is in use, applications must not send unencrypted DNS
414      * queries as doing so could reveal private user information. Furthermore,
415      * if private DNS is in use and {@link #getPrivateDnsServerName} is not
416      * {@code null}, DNS queries must be sent to the specified DNS server.
417      *
418      * @return {@code true} if private DNS is in use, {@code false} otherwise.
419      */
isPrivateDnsActive()420     public boolean isPrivateDnsActive() {
421         return mUsePrivateDns;
422     }
423 
424     /**
425      * Set the name of the private DNS server to which private DNS queries
426      * should be sent when in strict mode. This value should be {@code null}
427      * when private DNS is off or in opportunistic mode.
428      *
429      * @param privateDnsServerName The private DNS server name.
430      * @hide
431      */
setPrivateDnsServerName(@ullable String privateDnsServerName)432     public void setPrivateDnsServerName(@Nullable String privateDnsServerName) {
433         mPrivateDnsServerName = privateDnsServerName;
434     }
435 
436     /**
437      * Returns the private DNS server name that is in use. If not {@code null},
438      * private DNS is in strict mode. In this mode, applications should ensure
439      * that all DNS queries are encrypted and sent to this hostname and that
440      * queries are only sent if the hostname's certificate is valid. If
441      * {@code null} and {@link #isPrivateDnsActive} is {@code true}, private
442      * DNS is in opportunistic mode, and applications should ensure that DNS
443      * queries are encrypted and sent to a DNS server returned by
444      * {@link #getDnsServers}. System DNS will handle each of these cases
445      * correctly, but applications implementing their own DNS lookups must make
446      * sure to follow these requirements.
447      *
448      * @return The private DNS server name.
449      */
getPrivateDnsServerName()450     public @Nullable String getPrivateDnsServerName() {
451         return mPrivateDnsServerName;
452     }
453 
454     /**
455      * Adds the given {@link InetAddress} to the list of validated private DNS servers,
456      * if not present. This is distinct from the server name in that these are actually
457      * resolved addresses.
458      *
459      * @param dnsServer The {@link InetAddress} to add to the list of validated private DNS servers.
460      * @return true if the DNS server was added, false if it was already present.
461      * @hide
462      */
addValidatedPrivateDnsServer(InetAddress dnsServer)463     public boolean addValidatedPrivateDnsServer(InetAddress dnsServer) {
464         if (dnsServer != null && !mValidatedPrivateDnses.contains(dnsServer)) {
465             mValidatedPrivateDnses.add(dnsServer);
466             return true;
467         }
468         return false;
469     }
470 
471     /**
472      * Removes the given {@link InetAddress} from the list of validated private DNS servers.
473      *
474      * @param dnsServer The {@link InetAddress} to remove from the list of validated private DNS
475      *        servers.
476      * @return true if the DNS server was removed, false if it did not exist.
477      * @hide
478      */
removeValidatedPrivateDnsServer(InetAddress dnsServer)479     public boolean removeValidatedPrivateDnsServer(InetAddress dnsServer) {
480         if (dnsServer != null) {
481             return mValidatedPrivateDnses.remove(dnsServer);
482         }
483         return false;
484     }
485 
486     /**
487      * Replaces the validated private DNS servers in this {@code LinkProperties} with
488      * the given {@link Collection} of {@link InetAddress} objects.
489      *
490      * @param dnsServers The {@link Collection} of validated private DNS servers to set in this
491      *        object.
492      * @hide
493      */
setValidatedPrivateDnsServers(Collection<InetAddress> dnsServers)494     public void setValidatedPrivateDnsServers(Collection<InetAddress> dnsServers) {
495         mValidatedPrivateDnses.clear();
496         for (InetAddress dnsServer: dnsServers) {
497             addValidatedPrivateDnsServer(dnsServer);
498         }
499     }
500 
501     /**
502      * Returns all the {@link InetAddress} for validated private DNS servers on this link.
503      * These are resolved from the private DNS server name.
504      *
505      * @return An umodifiable {@link List} of {@link InetAddress} for validated private
506      *         DNS servers on this link.
507      * @hide
508      */
getValidatedPrivateDnsServers()509     public List<InetAddress> getValidatedPrivateDnsServers() {
510         return Collections.unmodifiableList(mValidatedPrivateDnses);
511     }
512 
513     /**
514      * Sets the DNS domain search path used on this link.
515      *
516      * @param domains A {@link String} listing in priority order the comma separated
517      *                domains to search when resolving host names on this link.
518      * @hide
519      */
setDomains(String domains)520     public void setDomains(String domains) {
521         mDomains = domains;
522     }
523 
524     /**
525      * Get the DNS domains search path set for this link.
526      *
527      * @return A {@link String} containing the comma separated domains to search when resolving
528      *         host names on this link.
529      */
getDomains()530     public String getDomains() {
531         return mDomains;
532     }
533 
534     /**
535      * Sets the Maximum Transmission Unit size to use on this link.  This should not be used
536      * unless the system default (1500) is incorrect.  Values less than 68 or greater than
537      * 10000 will be ignored.
538      *
539      * @param mtu The MTU to use for this link.
540      * @hide
541      */
setMtu(int mtu)542     public void setMtu(int mtu) {
543         mMtu = mtu;
544     }
545 
546     /**
547      * Gets any non-default MTU size set for this link.  Note that if the default is being used
548      * this will return 0.
549      *
550      * @return The mtu value set for this link.
551      * @hide
552      */
getMtu()553     public int getMtu() {
554         return mMtu;
555     }
556 
557     /**
558      * Sets the tcp buffers sizes to be used when this link is the system default.
559      * Should be of the form "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max".
560      *
561      * @param tcpBufferSizes The tcp buffers sizes to use.
562      *
563      * @hide
564      */
setTcpBufferSizes(String tcpBufferSizes)565     public void setTcpBufferSizes(String tcpBufferSizes) {
566         mTcpBufferSizes = tcpBufferSizes;
567     }
568 
569     /**
570      * Gets the tcp buffer sizes.
571      *
572      * @return the tcp buffer sizes to use when this link is the system default.
573      *
574      * @hide
575      */
getTcpBufferSizes()576     public String getTcpBufferSizes() {
577         return mTcpBufferSizes;
578     }
579 
routeWithInterface(RouteInfo route)580     private RouteInfo routeWithInterface(RouteInfo route) {
581         return new RouteInfo(
582             route.getDestination(),
583             route.getGateway(),
584             mIfaceName,
585             route.getType());
586     }
587 
588     /**
589      * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
590      * {@link RouteInfo} had an interface name set and that differs from the interface set for this
591      * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.  The proper
592      * course is to add either un-named or properly named {@link RouteInfo}.
593      *
594      * @param route A {@link RouteInfo} to add to this object.
595      * @return {@code false} if the route was already present, {@code true} if it was added.
596      *
597      * @hide
598      */
addRoute(RouteInfo route)599     public boolean addRoute(RouteInfo route) {
600         if (route != null) {
601             String routeIface = route.getInterface();
602             if (routeIface != null && !routeIface.equals(mIfaceName)) {
603                 throw new IllegalArgumentException(
604                    "Route added with non-matching interface: " + routeIface +
605                    " vs. " + mIfaceName);
606             }
607             route = routeWithInterface(route);
608             if (!mRoutes.contains(route)) {
609                 mRoutes.add(route);
610                 return true;
611             }
612         }
613         return false;
614     }
615 
616     /**
617      * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must
618      * specify an interface and the interface must match the interface of this
619      * {@code LinkProperties}, or it will not be removed.
620      *
621      * @return {@code true} if the route was removed, {@code false} if it was not present.
622      *
623      * @hide
624      */
removeRoute(RouteInfo route)625     public boolean removeRoute(RouteInfo route) {
626         return route != null &&
627                 Objects.equals(mIfaceName, route.getInterface()) &&
628                 mRoutes.remove(route);
629     }
630 
631     /**
632      * Returns all the {@link RouteInfo} set on this link.
633      *
634      * @return An unmodifiable {@link List} of {@link RouteInfo} for this link.
635      */
getRoutes()636     public List<RouteInfo> getRoutes() {
637         return Collections.unmodifiableList(mRoutes);
638     }
639 
640     /**
641      * Make sure this LinkProperties instance contains routes that cover the local subnet
642      * of its link addresses. Add any route that is missing.
643      * @hide
644      */
ensureDirectlyConnectedRoutes()645     public void ensureDirectlyConnectedRoutes() {
646         for (LinkAddress addr: mLinkAddresses) {
647             addRoute(new RouteInfo(addr, null, mIfaceName));
648         }
649     }
650 
651     /**
652      * Returns all the routes on this link and all the links stacked above it.
653      * @hide
654      */
getAllRoutes()655     public List<RouteInfo> getAllRoutes() {
656         List<RouteInfo> routes = new ArrayList<>();
657         routes.addAll(mRoutes);
658         for (LinkProperties stacked: mStackedLinks.values()) {
659             routes.addAll(stacked.getAllRoutes());
660         }
661         return routes;
662     }
663 
664     /**
665      * Sets the recommended {@link ProxyInfo} to use on this link, or {@code null} for none.
666      * Note that Http Proxies are only a hint - the system recommends their use, but it does
667      * not enforce it and applications may ignore them.
668      *
669      * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link.
670      * @hide
671      */
setHttpProxy(ProxyInfo proxy)672     public void setHttpProxy(ProxyInfo proxy) {
673         mHttpProxy = proxy;
674     }
675 
676     /**
677      * Gets the recommended {@link ProxyInfo} (or {@code null}) set on this link.
678      *
679      * @return The {@link ProxyInfo} set on this link
680      */
getHttpProxy()681     public ProxyInfo getHttpProxy() {
682         return mHttpProxy;
683     }
684 
685     /**
686      * Adds a stacked link.
687      *
688      * If there is already a stacked link with the same interfacename as link,
689      * that link is replaced with link. Otherwise, link is added to the list
690      * of stacked links. If link is null, nothing changes.
691      *
692      * @param link The link to add.
693      * @return true if the link was stacked, false otherwise.
694      * @hide
695      */
addStackedLink(LinkProperties link)696     public boolean addStackedLink(LinkProperties link) {
697         if (link != null && link.getInterfaceName() != null) {
698             mStackedLinks.put(link.getInterfaceName(), link);
699             return true;
700         }
701         return false;
702     }
703 
704     /**
705      * Removes a stacked link.
706      *
707      * If there is a stacked link with the given interface name, it is
708      * removed. Otherwise, nothing changes.
709      *
710      * @param iface The interface name of the link to remove.
711      * @return true if the link was removed, false otherwise.
712      * @hide
713      */
removeStackedLink(String iface)714     public boolean removeStackedLink(String iface) {
715         if (iface != null) {
716             LinkProperties removed = mStackedLinks.remove(iface);
717             return removed != null;
718         }
719         return false;
720     }
721 
722     /**
723      * Returns all the links stacked on top of this link.
724      * @hide
725      */
getStackedLinks()726     public @NonNull List<LinkProperties> getStackedLinks() {
727         if (mStackedLinks.isEmpty()) {
728             return Collections.EMPTY_LIST;
729         }
730         List<LinkProperties> stacked = new ArrayList<LinkProperties>();
731         for (LinkProperties link : mStackedLinks.values()) {
732             stacked.add(new LinkProperties(link));
733         }
734         return Collections.unmodifiableList(stacked);
735     }
736 
737     /**
738      * Clears this object to its initial state.
739      * @hide
740      */
clear()741     public void clear() {
742         mIfaceName = null;
743         mLinkAddresses.clear();
744         mDnses.clear();
745         mUsePrivateDns = false;
746         mPrivateDnsServerName = null;
747         mDomains = null;
748         mRoutes.clear();
749         mHttpProxy = null;
750         mStackedLinks.clear();
751         mMtu = 0;
752         mTcpBufferSizes = null;
753     }
754 
755     /**
756      * Implement the Parcelable interface
757      */
describeContents()758     public int describeContents() {
759         return 0;
760     }
761 
762     @Override
toString()763     public String toString() {
764         String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
765 
766         String linkAddresses = "LinkAddresses: [";
767         for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
768         linkAddresses += "] ";
769 
770         String dns = "DnsAddresses: [";
771         for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
772         dns += "] ";
773 
774         String usePrivateDns = "UsePrivateDns: " + mUsePrivateDns + " ";
775 
776         String privateDnsServerName = "";
777         if (privateDnsServerName != null) {
778             privateDnsServerName = "PrivateDnsServerName: " + mPrivateDnsServerName + " ";
779         }
780 
781         String validatedPrivateDns = "";
782         if (!mValidatedPrivateDnses.isEmpty()) {
783             validatedPrivateDns = "ValidatedPrivateDnsAddresses: [";
784             for (InetAddress addr : mValidatedPrivateDnses) {
785                 validatedPrivateDns += addr.getHostAddress() + ",";
786             }
787             validatedPrivateDns += "] ";
788         }
789 
790         String domainName = "Domains: " + mDomains;
791 
792         String mtu = " MTU: " + mMtu;
793 
794         String tcpBuffSizes = "";
795         if (mTcpBufferSizes != null) {
796             tcpBuffSizes = " TcpBufferSizes: " + mTcpBufferSizes;
797         }
798 
799         String routes = " Routes: [";
800         for (RouteInfo route : mRoutes) routes += route.toString() + ",";
801         routes += "] ";
802         String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " ");
803 
804         String stacked = "";
805         if (mStackedLinks.values().size() > 0) {
806             stacked += " Stacked: [";
807             for (LinkProperties link: mStackedLinks.values()) {
808                 stacked += " [" + link.toString() + " ],";
809             }
810             stacked += "] ";
811         }
812         return "{" + ifaceName + linkAddresses + routes + dns + usePrivateDns
813             + privateDnsServerName + domainName + mtu + tcpBuffSizes + proxy
814             + stacked + "}";
815     }
816 
817     /**
818      * Returns true if this link has an IPv4 address.
819      *
820      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
821      * @hide
822      */
hasIPv4Address()823     public boolean hasIPv4Address() {
824         for (LinkAddress address : mLinkAddresses) {
825             if (address.getAddress() instanceof Inet4Address) {
826                 return true;
827             }
828         }
829         return false;
830     }
831 
832     /**
833      * Returns true if this link or any of its stacked interfaces has an IPv4 address.
834      *
835      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
836      */
hasIPv4AddressOnInterface(String iface)837     private boolean hasIPv4AddressOnInterface(String iface) {
838         // mIfaceName can be null.
839         return (Objects.equals(iface, mIfaceName) && hasIPv4Address()) ||
840                 (iface != null && mStackedLinks.containsKey(iface) &&
841                         mStackedLinks.get(iface).hasIPv4Address());
842     }
843 
844     /**
845      * Returns true if this link has a global preferred IPv6 address.
846      *
847      * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
848      * @hide
849      */
hasGlobalIPv6Address()850     public boolean hasGlobalIPv6Address() {
851         for (LinkAddress address : mLinkAddresses) {
852           if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) {
853             return true;
854           }
855         }
856         return false;
857     }
858 
859     /**
860      * Returns true if this link has an IPv4 default route.
861      *
862      * @return {@code true} if there is an IPv4 default route, {@code false} otherwise.
863      * @hide
864      */
hasIPv4DefaultRoute()865     public boolean hasIPv4DefaultRoute() {
866         for (RouteInfo r : mRoutes) {
867             if (r.isIPv4Default()) {
868                 return true;
869             }
870         }
871         return false;
872     }
873 
874     /**
875      * Returns true if this link has an IPv6 default route.
876      *
877      * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
878      * @hide
879      */
hasIPv6DefaultRoute()880     public boolean hasIPv6DefaultRoute() {
881         for (RouteInfo r : mRoutes) {
882             if (r.isIPv6Default()) {
883                 return true;
884             }
885         }
886         return false;
887     }
888 
889     /**
890      * Returns true if this link has an IPv4 DNS server.
891      *
892      * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise.
893      * @hide
894      */
hasIPv4DnsServer()895     public boolean hasIPv4DnsServer() {
896         for (InetAddress ia : mDnses) {
897             if (ia instanceof Inet4Address) {
898                 return true;
899             }
900         }
901         return false;
902     }
903 
904     /**
905      * Returns true if this link has an IPv6 DNS server.
906      *
907      * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise.
908      * @hide
909      */
hasIPv6DnsServer()910     public boolean hasIPv6DnsServer() {
911         for (InetAddress ia : mDnses) {
912             if (ia instanceof Inet6Address) {
913                 return true;
914             }
915         }
916         return false;
917     }
918 
919     /**
920      * Returns true if this link is provisioned for global IPv4 connectivity.
921      * This requires an IP address, default route, and DNS server.
922      *
923      * @return {@code true} if the link is provisioned, {@code false} otherwise.
924      * @hide
925      */
isIPv4Provisioned()926     public boolean isIPv4Provisioned() {
927         return (hasIPv4Address() &&
928                 hasIPv4DefaultRoute() &&
929                 hasIPv4DnsServer());
930     }
931 
932     /**
933      * Returns true if this link is provisioned for global IPv6 connectivity.
934      * This requires an IP address, default route, and DNS server.
935      *
936      * @return {@code true} if the link is provisioned, {@code false} otherwise.
937      * @hide
938      */
isIPv6Provisioned()939     public boolean isIPv6Provisioned() {
940         return (hasGlobalIPv6Address() &&
941                 hasIPv6DefaultRoute() &&
942                 hasIPv6DnsServer());
943     }
944 
945     /**
946      * Returns true if this link is provisioned for global connectivity,
947      * for at least one Internet Protocol family.
948      *
949      * @return {@code true} if the link is provisioned, {@code false} otherwise.
950      * @hide
951      */
isProvisioned()952     public boolean isProvisioned() {
953         return (isIPv4Provisioned() || isIPv6Provisioned());
954     }
955 
956     /**
957      * Evaluate whether the {@link InetAddress} is considered reachable.
958      *
959      * @return {@code true} if the given {@link InetAddress} is considered reachable,
960      *         {@code false} otherwise.
961      * @hide
962      */
isReachable(InetAddress ip)963     public boolean isReachable(InetAddress ip) {
964         final List<RouteInfo> allRoutes = getAllRoutes();
965         // If we don't have a route to this IP address, it's not reachable.
966         final RouteInfo bestRoute = RouteInfo.selectBestRoute(allRoutes, ip);
967         if (bestRoute == null) {
968             return false;
969         }
970 
971         // TODO: better source address evaluation for destination addresses.
972 
973         if (ip instanceof Inet4Address) {
974             // For IPv4, it suffices for now to simply have any address.
975             return hasIPv4AddressOnInterface(bestRoute.getInterface());
976         } else if (ip instanceof Inet6Address) {
977             if (ip.isLinkLocalAddress()) {
978                 // For now, just make sure link-local destinations have
979                 // scopedIds set, since transmits will generally fail otherwise.
980                 // TODO: verify it matches the ifindex of one of the interfaces.
981                 return (((Inet6Address)ip).getScopeId() != 0);
982             }  else {
983                 // For non-link-local destinations check that either the best route
984                 // is directly connected or that some global preferred address exists.
985                 // TODO: reconsider all cases (disconnected ULA networks, ...).
986                 return (!bestRoute.hasGateway() || hasGlobalIPv6Address());
987             }
988         }
989 
990         return false;
991     }
992 
993     /**
994      * Compares this {@code LinkProperties} interface name against the target
995      *
996      * @param target LinkProperties to compare.
997      * @return {@code true} if both are identical, {@code false} otherwise.
998      * @hide
999      */
isIdenticalInterfaceName(LinkProperties target)1000     public boolean isIdenticalInterfaceName(LinkProperties target) {
1001         return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
1002     }
1003 
1004     /**
1005      * Compares this {@code LinkProperties} interface addresses against the target
1006      *
1007      * @param target LinkProperties to compare.
1008      * @return {@code true} if both are identical, {@code false} otherwise.
1009      * @hide
1010      */
isIdenticalAddresses(LinkProperties target)1011     public boolean isIdenticalAddresses(LinkProperties target) {
1012         Collection<InetAddress> targetAddresses = target.getAddresses();
1013         Collection<InetAddress> sourceAddresses = getAddresses();
1014         return (sourceAddresses.size() == targetAddresses.size()) ?
1015                     sourceAddresses.containsAll(targetAddresses) : false;
1016     }
1017 
1018     /**
1019      * Compares this {@code LinkProperties} DNS addresses against the target
1020      *
1021      * @param target LinkProperties to compare.
1022      * @return {@code true} if both are identical, {@code false} otherwise.
1023      * @hide
1024      */
isIdenticalDnses(LinkProperties target)1025     public boolean isIdenticalDnses(LinkProperties target) {
1026         Collection<InetAddress> targetDnses = target.getDnsServers();
1027         String targetDomains = target.getDomains();
1028         if (mDomains == null) {
1029             if (targetDomains != null) return false;
1030         } else {
1031             if (mDomains.equals(targetDomains) == false) return false;
1032         }
1033         return (mDnses.size() == targetDnses.size()) ?
1034                 mDnses.containsAll(targetDnses) : false;
1035     }
1036 
1037     /**
1038      * Compares this {@code LinkProperties} private DNS settings against the
1039      * target.
1040      *
1041      * @param target LinkProperties to compare.
1042      * @return {@code true} if both are identical, {@code false} otherwise.
1043      * @hide
1044      */
isIdenticalPrivateDns(LinkProperties target)1045     public boolean isIdenticalPrivateDns(LinkProperties target) {
1046         return (isPrivateDnsActive() == target.isPrivateDnsActive()
1047                 && TextUtils.equals(getPrivateDnsServerName(),
1048                 target.getPrivateDnsServerName()));
1049     }
1050 
1051     /**
1052      * Compares this {@code LinkProperties} validated private DNS addresses against
1053      * the target
1054      *
1055      * @param target LinkProperties to compare.
1056      * @return {@code true} if both are identical, {@code false} otherwise.
1057      * @hide
1058      */
isIdenticalValidatedPrivateDnses(LinkProperties target)1059     public boolean isIdenticalValidatedPrivateDnses(LinkProperties target) {
1060         Collection<InetAddress> targetDnses = target.getValidatedPrivateDnsServers();
1061         return (mValidatedPrivateDnses.size() == targetDnses.size())
1062                 ? mValidatedPrivateDnses.containsAll(targetDnses) : false;
1063     }
1064 
1065     /**
1066      * Compares this {@code LinkProperties} Routes against the target
1067      *
1068      * @param target LinkProperties to compare.
1069      * @return {@code true} if both are identical, {@code false} otherwise.
1070      * @hide
1071      */
isIdenticalRoutes(LinkProperties target)1072     public boolean isIdenticalRoutes(LinkProperties target) {
1073         Collection<RouteInfo> targetRoutes = target.getRoutes();
1074         return (mRoutes.size() == targetRoutes.size()) ?
1075                 mRoutes.containsAll(targetRoutes) : false;
1076     }
1077 
1078     /**
1079      * Compares this {@code LinkProperties} HttpProxy against the target
1080      *
1081      * @param target LinkProperties to compare.
1082      * @return {@code true} if both are identical, {@code false} otherwise.
1083      * @hide
1084      */
isIdenticalHttpProxy(LinkProperties target)1085     public boolean isIdenticalHttpProxy(LinkProperties target) {
1086         return getHttpProxy() == null ? target.getHttpProxy() == null :
1087                 getHttpProxy().equals(target.getHttpProxy());
1088     }
1089 
1090     /**
1091      * Compares this {@code LinkProperties} stacked links against the target
1092      *
1093      * @param target LinkProperties to compare.
1094      * @return {@code true} if both are identical, {@code false} otherwise.
1095      * @hide
1096      */
isIdenticalStackedLinks(LinkProperties target)1097     public boolean isIdenticalStackedLinks(LinkProperties target) {
1098         if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) {
1099             return false;
1100         }
1101         for (LinkProperties stacked : mStackedLinks.values()) {
1102             // Hashtable values can never be null.
1103             String iface = stacked.getInterfaceName();
1104             if (!stacked.equals(target.mStackedLinks.get(iface))) {
1105                 return false;
1106             }
1107         }
1108         return true;
1109     }
1110 
1111     /**
1112      * Compares this {@code LinkProperties} MTU against the target
1113      *
1114      * @param target LinkProperties to compare.
1115      * @return {@code true} if both are identical, {@code false} otherwise.
1116      * @hide
1117      */
isIdenticalMtu(LinkProperties target)1118     public boolean isIdenticalMtu(LinkProperties target) {
1119         return getMtu() == target.getMtu();
1120     }
1121 
1122     /**
1123      * Compares this {@code LinkProperties} Tcp buffer sizes against the target.
1124      *
1125      * @param target LinkProperties to compare.
1126      * @return {@code true} if both are identical, {@code false} otherwise.
1127      * @hide
1128      */
isIdenticalTcpBufferSizes(LinkProperties target)1129     public boolean isIdenticalTcpBufferSizes(LinkProperties target) {
1130         return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes);
1131     }
1132 
1133     @Override
1134     /**
1135      * Compares this {@code LinkProperties} instance against the target
1136      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
1137      * all their fields are equal in values.
1138      *
1139      * For collection fields, such as mDnses, containsAll() is used to check
1140      * if two collections contains the same elements, independent of order.
1141      * There are two thoughts regarding containsAll()
1142      * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
1143      * 2. Worst case performance is O(n^2).
1144      *
1145      * @param obj the object to be tested for equality.
1146      * @return {@code true} if both objects are equal, {@code false} otherwise.
1147      */
equals(Object obj)1148     public boolean equals(Object obj) {
1149         if (this == obj) return true;
1150 
1151         if (!(obj instanceof LinkProperties)) return false;
1152 
1153         LinkProperties target = (LinkProperties) obj;
1154         /**
1155          * This method does not check that stacked interfaces are equal, because
1156          * stacked interfaces are not so much a property of the link as a
1157          * description of connections between links.
1158          */
1159         return isIdenticalInterfaceName(target)
1160                 && isIdenticalAddresses(target)
1161                 && isIdenticalDnses(target)
1162                 && isIdenticalPrivateDns(target)
1163                 && isIdenticalValidatedPrivateDnses(target)
1164                 && isIdenticalRoutes(target)
1165                 && isIdenticalHttpProxy(target)
1166                 && isIdenticalStackedLinks(target)
1167                 && isIdenticalMtu(target)
1168                 && isIdenticalTcpBufferSizes(target);
1169     }
1170 
1171     /**
1172      * Compares the addresses in this LinkProperties with another
1173      * LinkProperties, examining only addresses on the base link.
1174      *
1175      * @param target a LinkProperties with the new list of addresses
1176      * @return the differences between the addresses.
1177      * @hide
1178      */
compareAddresses(LinkProperties target)1179     public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
1180         /*
1181          * Duplicate the LinkAddresses into removed, we will be removing
1182          * address which are common between mLinkAddresses and target
1183          * leaving the addresses that are different. And address which
1184          * are in target but not in mLinkAddresses are placed in the
1185          * addedAddresses.
1186          */
1187         return new CompareResult<>(mLinkAddresses,
1188                 target != null ? target.getLinkAddresses() : null);
1189     }
1190 
1191     /**
1192      * Compares the DNS addresses in this LinkProperties with another
1193      * LinkProperties, examining only DNS addresses on the base link.
1194      *
1195      * @param target a LinkProperties with the new list of dns addresses
1196      * @return the differences between the DNS addresses.
1197      * @hide
1198      */
compareDnses(LinkProperties target)1199     public CompareResult<InetAddress> compareDnses(LinkProperties target) {
1200         /*
1201          * Duplicate the InetAddresses into removed, we will be removing
1202          * dns address which are common between mDnses and target
1203          * leaving the addresses that are different. And dns address which
1204          * are in target but not in mDnses are placed in the
1205          * addedAddresses.
1206          */
1207         return new CompareResult<>(mDnses, target != null ? target.getDnsServers() : null);
1208     }
1209 
1210     /**
1211      * Compares the validated private DNS addresses in this LinkProperties with another
1212      * LinkProperties.
1213      *
1214      * @param target a LinkProperties with the new list of validated private dns addresses
1215      * @return the differences between the DNS addresses.
1216      * @hide
1217      */
compareValidatedPrivateDnses(LinkProperties target)1218     public CompareResult<InetAddress> compareValidatedPrivateDnses(LinkProperties target) {
1219         return new CompareResult<>(mValidatedPrivateDnses,
1220                 target != null ? target.getValidatedPrivateDnsServers() : null);
1221     }
1222 
1223     /**
1224      * Compares all routes in this LinkProperties with another LinkProperties,
1225      * examining both the the base link and all stacked links.
1226      *
1227      * @param target a LinkProperties with the new list of routes
1228      * @return the differences between the routes.
1229      * @hide
1230      */
compareAllRoutes(LinkProperties target)1231     public CompareResult<RouteInfo> compareAllRoutes(LinkProperties target) {
1232         /*
1233          * Duplicate the RouteInfos into removed, we will be removing
1234          * routes which are common between mRoutes and target
1235          * leaving the routes that are different. And route address which
1236          * are in target but not in mRoutes are placed in added.
1237          */
1238         return new CompareResult<>(getAllRoutes(), target != null ? target.getAllRoutes() : null);
1239     }
1240 
1241     /**
1242      * Compares all interface names in this LinkProperties with another
1243      * LinkProperties, examining both the the base link and all stacked links.
1244      *
1245      * @param target a LinkProperties with the new list of interface names
1246      * @return the differences between the interface names.
1247      * @hide
1248      */
compareAllInterfaceNames(LinkProperties target)1249     public CompareResult<String> compareAllInterfaceNames(LinkProperties target) {
1250         /*
1251          * Duplicate the interface names into removed, we will be removing
1252          * interface names which are common between this and target
1253          * leaving the interface names that are different. And interface names which
1254          * are in target but not in this are placed in added.
1255          */
1256         return new CompareResult<>(getAllInterfaceNames(),
1257                 target != null ? target.getAllInterfaceNames() : null);
1258     }
1259 
1260 
1261     @Override
1262     /**
1263      * generate hashcode based on significant fields
1264      * Equal objects must produce the same hash code, while unequal objects
1265      * may have the same hash codes.
1266      */
hashCode()1267     public int hashCode() {
1268         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
1269                 + mLinkAddresses.size() * 31
1270                 + mDnses.size() * 37
1271                 + mValidatedPrivateDnses.size() * 61
1272                 + ((null == mDomains) ? 0 : mDomains.hashCode())
1273                 + mRoutes.size() * 41
1274                 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
1275                 + mStackedLinks.hashCode() * 47)
1276                 + mMtu * 51
1277                 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
1278                 + (mUsePrivateDns ? 57 : 0)
1279                 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode());
1280     }
1281 
1282     /**
1283      * Implement the Parcelable interface.
1284      */
writeToParcel(Parcel dest, int flags)1285     public void writeToParcel(Parcel dest, int flags) {
1286         dest.writeString(getInterfaceName());
1287         dest.writeInt(mLinkAddresses.size());
1288         for (LinkAddress linkAddress : mLinkAddresses) {
1289             dest.writeParcelable(linkAddress, flags);
1290         }
1291 
1292         dest.writeInt(mDnses.size());
1293         for (InetAddress d : mDnses) {
1294             dest.writeByteArray(d.getAddress());
1295         }
1296         dest.writeInt(mValidatedPrivateDnses.size());
1297         for (InetAddress d : mValidatedPrivateDnses) {
1298             dest.writeByteArray(d.getAddress());
1299         }
1300         dest.writeBoolean(mUsePrivateDns);
1301         dest.writeString(mPrivateDnsServerName);
1302         dest.writeString(mDomains);
1303         dest.writeInt(mMtu);
1304         dest.writeString(mTcpBufferSizes);
1305         dest.writeInt(mRoutes.size());
1306         for (RouteInfo route : mRoutes) {
1307             dest.writeParcelable(route, flags);
1308         }
1309 
1310         if (mHttpProxy != null) {
1311             dest.writeByte((byte)1);
1312             dest.writeParcelable(mHttpProxy, flags);
1313         } else {
1314             dest.writeByte((byte)0);
1315         }
1316         ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values());
1317         dest.writeList(stackedLinks);
1318     }
1319 
1320     /**
1321      * Implement the Parcelable interface.
1322      */
1323     public static final Creator<LinkProperties> CREATOR =
1324         new Creator<LinkProperties>() {
1325             public LinkProperties createFromParcel(Parcel in) {
1326                 LinkProperties netProp = new LinkProperties();
1327 
1328                 String iface = in.readString();
1329                 if (iface != null) {
1330                     netProp.setInterfaceName(iface);
1331                 }
1332                 int addressCount = in.readInt();
1333                 for (int i = 0; i < addressCount; i++) {
1334                     netProp.addLinkAddress((LinkAddress) in.readParcelable(null));
1335                 }
1336                 addressCount = in.readInt();
1337                 for (int i = 0; i < addressCount; i++) {
1338                     try {
1339                         netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
1340                     } catch (UnknownHostException e) { }
1341                 }
1342                 addressCount = in.readInt();
1343                 for (int i = 0; i < addressCount; i++) {
1344                     try {
1345                         netProp.addValidatedPrivateDnsServer(
1346                                 InetAddress.getByAddress(in.createByteArray()));
1347                     } catch (UnknownHostException e) { }
1348                 }
1349                 netProp.setUsePrivateDns(in.readBoolean());
1350                 netProp.setPrivateDnsServerName(in.readString());
1351                 netProp.setDomains(in.readString());
1352                 netProp.setMtu(in.readInt());
1353                 netProp.setTcpBufferSizes(in.readString());
1354                 addressCount = in.readInt();
1355                 for (int i = 0; i < addressCount; i++) {
1356                     netProp.addRoute((RouteInfo) in.readParcelable(null));
1357                 }
1358                 if (in.readByte() == 1) {
1359                     netProp.setHttpProxy((ProxyInfo) in.readParcelable(null));
1360                 }
1361                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
1362                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
1363                 for (LinkProperties stackedLink: stackedLinks) {
1364                     netProp.addStackedLink(stackedLink);
1365                 }
1366                 return netProp;
1367             }
1368 
1369             public LinkProperties[] newArray(int size) {
1370                 return new LinkProperties[size];
1371             }
1372         };
1373 
1374     /**
1375      * Check the valid MTU range based on IPv4 or IPv6.
1376      * @hide
1377      */
isValidMtu(int mtu, boolean ipv6)1378     public static boolean isValidMtu(int mtu, boolean ipv6) {
1379         if (ipv6) {
1380             if (mtu >= MIN_MTU_V6 && mtu <= MAX_MTU) return true;
1381         } else {
1382             if (mtu >= MIN_MTU && mtu <= MAX_MTU) return true;
1383         }
1384         return false;
1385     }
1386 }
1387