• 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 android.net.ipmemorystore;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk;
22 
23 import com.android.internal.annotations.VisibleForTesting;
24 
25 import java.net.Inet4Address;
26 import java.net.InetAddress;
27 import java.net.UnknownHostException;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Objects;
32 import java.util.StringJoiner;
33 
34 /**
35  * A POD object to represent attributes of a single L2 network entry.
36  * @hide
37  */
38 public class NetworkAttributes {
39     private static final boolean DBG = true;
40 
41     // Weight cutoff for grouping. To group, a similarity score is computed with the following
42     // algorithm : if both fields are non-null and equals() then add their assigned weight, else if
43     // both are null then add a portion of their assigned weight (see NULL_MATCH_WEIGHT),
44     // otherwise add nothing.
45     // As a guideline, this should be something like 60~75% of the total weights in this class. The
46     // design states "in essence a reader should imagine that if two important columns don't match,
47     // or one important and several unimportant columns don't match then the two records are
48     // considered a different group".
49     private static final float TOTAL_WEIGHT_CUTOFF = 520.0f;
50     // The portion of the weight that is earned when scoring group-sameness by having both columns
51     // being null. This is because some networks rightfully don't have some attributes (e.g. a
52     // V6-only network won't have an assigned V4 address) and both being null should count for
53     // something, but attributes may also be null just because data is unavailable.
54     private static final float NULL_MATCH_WEIGHT = 0.25f;
55 
56     // The v4 address that was assigned to this device the last time it joined this network.
57     // This typically comes from DHCP but could be something else like static configuration.
58     // This does not apply to IPv6.
59     // TODO : add a list of v6 prefixes for the v6 case.
60     @Nullable
61     public final Inet4Address assignedV4Address;
62     private static final float WEIGHT_ASSIGNEDV4ADDR = 300.0f;
63 
64     // The lease expiry timestamp of v4 address allocated from DHCP server, in milliseconds.
65     @Nullable
66     public final Long assignedV4AddressExpiry;
67     // lease expiry doesn't imply any correlation between "the same lease expiry value" and "the
68     // same L3 network".
69     private static final float WEIGHT_ASSIGNEDV4ADDREXPIRY = 0.0f;
70 
71     // Optionally supplied by the client to signify belonging to a notion of a group owned by
72     // the client. For example, this could be a hash of the SSID on WiFi.
73     @Nullable
74     public final String cluster;
75     private static final float WEIGHT_CLUSTER = 300.0f;
76 
77     // The list of DNS server addresses.
78     @Nullable
79     public final List<InetAddress> dnsAddresses;
80     private static final float WEIGHT_DNSADDRESSES = 200.0f;
81 
82     // The mtu on this network.
83     @Nullable
84     public final Integer mtu;
85     private static final float WEIGHT_MTU = 50.0f;
86 
87     // IPv6 provisioning quirk info about this network, if applicable.
88     @Nullable
89     public final IPv6ProvisioningLossQuirk ipv6ProvisioningLossQuirk;
90     // quirk information doesn't imply any correlation between "the same quirk detection count and
91     // expiry" and "the same L3 network".
92     private static final float WEIGHT_V6PROVLOSSQUIRK = 0.0f;
93 
94     // The sum of all weights in this class. Tests ensure that this stays equal to the total of
95     // all weights.
96     /** @hide */
97     @VisibleForTesting
98     public static final float TOTAL_WEIGHT = WEIGHT_ASSIGNEDV4ADDR
99             + WEIGHT_ASSIGNEDV4ADDREXPIRY
100             + WEIGHT_CLUSTER
101             + WEIGHT_DNSADDRESSES
102             + WEIGHT_MTU
103             + WEIGHT_V6PROVLOSSQUIRK;
104 
105     /** @hide */
106     @VisibleForTesting
NetworkAttributes( @ullable final Inet4Address assignedV4Address, @Nullable final Long assignedV4AddressExpiry, @Nullable final String cluster, @Nullable final List<InetAddress> dnsAddresses, @Nullable final Integer mtu, @Nullable final IPv6ProvisioningLossQuirk ipv6ProvisioningLossQuirk)107     public NetworkAttributes(
108             @Nullable final Inet4Address assignedV4Address,
109             @Nullable final Long assignedV4AddressExpiry,
110             @Nullable final String cluster,
111             @Nullable final List<InetAddress> dnsAddresses,
112             @Nullable final Integer mtu,
113             @Nullable final IPv6ProvisioningLossQuirk ipv6ProvisioningLossQuirk) {
114         if (mtu != null && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
115         if (assignedV4AddressExpiry != null && assignedV4AddressExpiry <= 0) {
116             throw new IllegalArgumentException("lease expiry can't be negative or zero");
117         }
118         this.assignedV4Address = assignedV4Address;
119         this.assignedV4AddressExpiry = assignedV4AddressExpiry;
120         this.cluster = cluster;
121         this.dnsAddresses = null == dnsAddresses ? null :
122                 Collections.unmodifiableList(new ArrayList<>(dnsAddresses));
123         this.mtu = mtu;
124         this.ipv6ProvisioningLossQuirk = ipv6ProvisioningLossQuirk;
125     }
126 
127     @VisibleForTesting
NetworkAttributes(@onNull final NetworkAttributesParcelable parcelable)128     public NetworkAttributes(@NonNull final NetworkAttributesParcelable parcelable) {
129         // The call to the other constructor must be the first statement of this constructor,
130         // so everything has to be inline
131         this((Inet4Address) getByAddressOrNull(parcelable.assignedV4Address),
132                 parcelable.assignedV4AddressExpiry > 0
133                         ? parcelable.assignedV4AddressExpiry : null,
134                 parcelable.cluster,
135                 blobArrayToInetAddressList(parcelable.dnsAddresses),
136                 parcelable.mtu >= 0 ? parcelable.mtu : null,
137                 IPv6ProvisioningLossQuirk.fromStableParcelable(
138                         parcelable.ipv6ProvisioningLossQuirk));
139     }
140 
141     @Nullable
getByAddressOrNull(@ullable final byte[] address)142     private static InetAddress getByAddressOrNull(@Nullable final byte[] address) {
143         if (null == address) return null;
144         try {
145             return InetAddress.getByAddress(address);
146         } catch (UnknownHostException e) {
147             return null;
148         }
149     }
150 
151     @Nullable
blobArrayToInetAddressList(@ullable final Blob[] blobs)152     private static List<InetAddress> blobArrayToInetAddressList(@Nullable final Blob[] blobs) {
153         if (null == blobs) return null;
154         final ArrayList<InetAddress> list = new ArrayList<>(blobs.length);
155         for (final Blob b : blobs) {
156             final InetAddress addr = getByAddressOrNull(b.data);
157             if (null != addr) list.add(addr);
158         }
159         return list;
160     }
161 
162     @Nullable
inetAddressListToBlobArray(@ullable final List<InetAddress> addresses)163     private static Blob[] inetAddressListToBlobArray(@Nullable final List<InetAddress> addresses) {
164         if (null == addresses) return null;
165         final ArrayList<Blob> blobs = new ArrayList<>();
166         for (int i = 0; i < addresses.size(); ++i) {
167             final InetAddress addr = addresses.get(i);
168             if (null == addr) continue;
169             final Blob b = new Blob();
170             b.data = addr.getAddress();
171             blobs.add(b);
172         }
173         return blobs.toArray(new Blob[0]);
174     }
175 
176     /** Converts this NetworkAttributes to a parcelable object */
177     @NonNull
toParcelable()178     public NetworkAttributesParcelable toParcelable() {
179         final NetworkAttributesParcelable parcelable = new NetworkAttributesParcelable();
180         parcelable.assignedV4Address =
181                 (null == assignedV4Address) ? null : assignedV4Address.getAddress();
182         parcelable.assignedV4AddressExpiry =
183                 (null == assignedV4AddressExpiry) ? 0 : assignedV4AddressExpiry;
184         parcelable.cluster = cluster;
185         parcelable.dnsAddresses = inetAddressListToBlobArray(dnsAddresses);
186         parcelable.mtu = (null == mtu) ? -1 : mtu;
187         parcelable.ipv6ProvisioningLossQuirk = (null == ipv6ProvisioningLossQuirk)
188                 ? null : ipv6ProvisioningLossQuirk.toStableParcelable();
189         return parcelable;
190     }
191 
samenessContribution(final float weight, @Nullable final Object o1, @Nullable final Object o2)192     private float samenessContribution(final float weight,
193             @Nullable final Object o1, @Nullable final Object o2) {
194         if (null == o1) {
195             return (null == o2) ? weight * NULL_MATCH_WEIGHT : 0f;
196         }
197         return Objects.equals(o1, o2) ? weight : 0f;
198     }
199 
200     /** @hide */
getNetworkGroupSamenessConfidence(@onNull final NetworkAttributes o)201     public float getNetworkGroupSamenessConfidence(@NonNull final NetworkAttributes o) {
202         // TODO: Remove the useless comparison for members which are associated with 0 weight.
203         final float samenessScore =
204                 samenessContribution(WEIGHT_ASSIGNEDV4ADDR, assignedV4Address, o.assignedV4Address)
205                 + samenessContribution(WEIGHT_ASSIGNEDV4ADDREXPIRY, assignedV4AddressExpiry,
206                       o.assignedV4AddressExpiry)
207                 + samenessContribution(WEIGHT_CLUSTER, cluster, o.cluster)
208                 + samenessContribution(WEIGHT_DNSADDRESSES, dnsAddresses, o.dnsAddresses)
209                 + samenessContribution(WEIGHT_MTU, mtu, o.mtu)
210                 + samenessContribution(WEIGHT_V6PROVLOSSQUIRK, ipv6ProvisioningLossQuirk,
211                       o.ipv6ProvisioningLossQuirk);
212         // The minimum is 0, the max is TOTAL_WEIGHT and should be represented by 1.0, and
213         // TOTAL_WEIGHT_CUTOFF should represent 0.5, but there is no requirement that
214         // TOTAL_WEIGHT_CUTOFF would be half of TOTAL_WEIGHT (indeed, it should not be).
215         // So scale scores under the cutoff between 0 and 0.5, and the scores over the cutoff
216         // between 0.5 and 1.0.
217         if (samenessScore < TOTAL_WEIGHT_CUTOFF) {
218             return samenessScore / (TOTAL_WEIGHT_CUTOFF * 2);
219         } else {
220             return (samenessScore - TOTAL_WEIGHT_CUTOFF) / (TOTAL_WEIGHT - TOTAL_WEIGHT_CUTOFF) / 2
221                     + 0.5f;
222         }
223     }
224 
225     /** @hide */
226     public static class Builder {
227         @Nullable
228         private Inet4Address mAssignedAddress;
229         @Nullable
230         private Long mAssignedAddressExpiry;
231         @Nullable
232         private String mCluster;
233         @Nullable
234         private List<InetAddress> mDnsAddresses;
235         @Nullable
236         private Integer mMtu;
237         @Nullable
238         private IPv6ProvisioningLossQuirk mIpv6ProvLossQuirk;
239 
240         /**
241          * Constructs a new Builder.
242          */
Builder()243         public Builder() {}
244 
245         /**
246          * Constructs a Builder from the passed NetworkAttributes.
247          */
Builder(@onNull final NetworkAttributes attributes)248         public Builder(@NonNull final NetworkAttributes attributes) {
249             mAssignedAddress = attributes.assignedV4Address;
250             mAssignedAddressExpiry = attributes.assignedV4AddressExpiry;
251             mCluster = attributes.cluster;
252             mDnsAddresses = new ArrayList<>(attributes.dnsAddresses);
253             mMtu = attributes.mtu;
254             mIpv6ProvLossQuirk = attributes.ipv6ProvisioningLossQuirk;
255         }
256 
257         /**
258          * Set the assigned address.
259          * @param assignedV4Address The assigned address.
260          * @return This builder.
261          */
setAssignedV4Address(@ullable final Inet4Address assignedV4Address)262         public Builder setAssignedV4Address(@Nullable final Inet4Address assignedV4Address) {
263             mAssignedAddress = assignedV4Address;
264             return this;
265         }
266 
267         /**
268          * Set the lease expiry timestamp of assigned v4 address. Long.MAX_VALUE is used
269          * to represent "infinite lease".
270          *
271          * @param assignedV4AddressExpiry The lease expiry timestamp of assigned v4 address.
272          * @return This builder.
273          */
setAssignedV4AddressExpiry( @ullable final Long assignedV4AddressExpiry)274         public Builder setAssignedV4AddressExpiry(
275                 @Nullable final Long assignedV4AddressExpiry) {
276             if (null != assignedV4AddressExpiry && assignedV4AddressExpiry <= 0) {
277                 throw new IllegalArgumentException("lease expiry can't be negative or zero");
278             }
279             mAssignedAddressExpiry = assignedV4AddressExpiry;
280             return this;
281         }
282 
283         /**
284          * Set the cluster.
285          * @param cluster The cluster.
286          * @return This builder.
287          */
setCluster(@ullable final String cluster)288         public Builder setCluster(@Nullable final String cluster) {
289             mCluster = cluster;
290             return this;
291         }
292 
293         /**
294          * Set the DNS addresses.
295          * @param dnsAddresses The DNS addresses.
296          * @return This builder.
297          */
setDnsAddresses(@ullable final List<InetAddress> dnsAddresses)298         public Builder setDnsAddresses(@Nullable final List<InetAddress> dnsAddresses) {
299             if (DBG && null != dnsAddresses) {
300                 // Parceling code crashes if one of the addresses is null, therefore validate
301                 // them when running in debug.
302                 for (final InetAddress address : dnsAddresses) {
303                     if (null == address) throw new IllegalArgumentException("Null DNS address");
304                 }
305             }
306             this.mDnsAddresses = dnsAddresses;
307             return this;
308         }
309 
310         /**
311          * Set the MTU.
312          * @param mtu The MTU.
313          * @return This builder.
314          */
setMtu(@ullable final Integer mtu)315         public Builder setMtu(@Nullable final Integer mtu) {
316             if (null != mtu && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
317             mMtu = mtu;
318             return this;
319         }
320 
321         /**
322          * Set the IPv6 Provisioning Loss Quirk information.
323          * @param quirk The IPv6 Provisioning Loss Quirk.
324          * @return This builder.
325          */
setIpv6ProvLossQuirk(@ullable final IPv6ProvisioningLossQuirk quirk)326         public Builder setIpv6ProvLossQuirk(@Nullable final IPv6ProvisioningLossQuirk quirk) {
327             mIpv6ProvLossQuirk = quirk;
328             return this;
329         }
330 
331         /**
332          * Return the built NetworkAttributes object.
333          * @return The built NetworkAttributes object.
334          */
build()335         public NetworkAttributes build() {
336             return new NetworkAttributes(mAssignedAddress, mAssignedAddressExpiry,
337                     mCluster, mDnsAddresses, mMtu, mIpv6ProvLossQuirk);
338         }
339     }
340 
341     /** @hide */
isEmpty()342     public boolean isEmpty() {
343         return (null == assignedV4Address) && (null == assignedV4AddressExpiry)
344                 && (null == cluster) && (null == dnsAddresses) && (null == mtu)
345                 && (null == ipv6ProvisioningLossQuirk);
346     }
347 
348     @Override
equals(@ullable final Object o)349     public boolean equals(@Nullable final Object o) {
350         if (!(o instanceof NetworkAttributes)) return false;
351         final NetworkAttributes other = (NetworkAttributes) o;
352         return Objects.equals(assignedV4Address, other.assignedV4Address)
353                 && Objects.equals(assignedV4AddressExpiry, other.assignedV4AddressExpiry)
354                 && Objects.equals(cluster, other.cluster)
355                 && Objects.equals(dnsAddresses, other.dnsAddresses)
356                 && Objects.equals(mtu, other.mtu)
357                 && Objects.equals(ipv6ProvisioningLossQuirk, other.ipv6ProvisioningLossQuirk);
358     }
359 
360     @Override
hashCode()361     public int hashCode() {
362         return Objects.hash(assignedV4Address, assignedV4AddressExpiry,
363                 cluster, dnsAddresses, mtu, ipv6ProvisioningLossQuirk);
364     }
365 
366     /** Pretty print */
367     @Override
toString()368     public String toString() {
369         final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}");
370         final ArrayList<String> nullFields = new ArrayList<>();
371 
372         if (null != assignedV4Address) {
373             resultJoiner.add("assignedV4Addr :");
374             resultJoiner.add(assignedV4Address.toString());
375         } else {
376             nullFields.add("assignedV4Addr");
377         }
378 
379         if (null != assignedV4AddressExpiry) {
380             resultJoiner.add("assignedV4AddressExpiry :");
381             resultJoiner.add(assignedV4AddressExpiry.toString());
382         } else {
383             nullFields.add("assignedV4AddressExpiry");
384         }
385 
386         if (null != cluster) {
387             resultJoiner.add("cluster :");
388             resultJoiner.add(cluster);
389         } else {
390             nullFields.add("cluster");
391         }
392 
393         if (null != dnsAddresses) {
394             resultJoiner.add("dnsAddr : [");
395             for (final InetAddress addr : dnsAddresses) {
396                 resultJoiner.add(addr.getHostAddress());
397             }
398             resultJoiner.add("]");
399         } else {
400             nullFields.add("dnsAddr");
401         }
402 
403         if (null != mtu) {
404             resultJoiner.add("mtu :");
405             resultJoiner.add(mtu.toString());
406         } else {
407             nullFields.add("mtu");
408         }
409 
410         if (null != ipv6ProvisioningLossQuirk) {
411             resultJoiner.add("ipv6ProvisioningLossQuirk : [");
412             resultJoiner.add(ipv6ProvisioningLossQuirk.toString());
413             resultJoiner.add("]");
414         } else {
415             nullFields.add("ipv6ProvisioningLossQuirk");
416         }
417 
418         if (!nullFields.isEmpty()) {
419             resultJoiner.add("; Null fields : [");
420             for (final String field : nullFields) {
421                 resultJoiner.add(field);
422             }
423             resultJoiner.add("]");
424         }
425 
426         return resultJoiner.toString();
427     }
428 }
429