Add new network quirk parcelable in the NetworkAttributes. am: 55d51e04b0 am: f01dcfc1e3

Original change: https://android-review.googlesource.com/c/platform/packages/modules/NetworkStack/+/1323251

Change-Id: I2a9b1ad06b19964539d4b3663ef1e6a688f476e4
diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp
index 0c513e1..10f2868 100644
--- a/common/networkstackclient/Android.bp
+++ b/common/networkstackclient/Android.bp
@@ -22,6 +22,7 @@
         "src/android/net/IIpMemoryStore.aidl",
         "src/android/net/IIpMemoryStoreCallbacks.aidl",
         "src/android/net/ipmemorystore/**/*.aidl",
+        "src/android/net/quirks/IPv6ProvisioningLossQuirkParcelable.aidl",
     ],
     backend: {
         java: {
@@ -132,6 +133,7 @@
         "src/android/net/IpMemoryStoreClient.java",
         "src/android/net/ipmemorystore/**/*.java",
         "src/android/net/networkstack/**/*.java",
+        "src/android/net/quirks/**/*.java",
         "src/android/net/shared/**/*.java",
     ],
     static_libs: [
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/current/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/current/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
index 92a570d..6b70a80 100644
--- a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/current/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/current/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
@@ -23,4 +23,5 @@
   String cluster;
   android.net.ipmemorystore.Blob[] dnsAddresses;
   int mtu;
+  @nullable android.net.quirks.IPv6ProvisioningLossQuirkParcelable ipv6ProvLossQuirk;
 }
diff --git a/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/current/android/net/quirks/IPv6ProvisioningLossQuirkParcelable.aidl b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/current/android/net/quirks/IPv6ProvisioningLossQuirkParcelable.aidl
new file mode 100644
index 0000000..a16908f
--- /dev/null
+++ b/common/networkstackclient/aidl_api/ipmemorystore-aidl-interfaces/current/android/net/quirks/IPv6ProvisioningLossQuirkParcelable.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.quirks;
+parcelable IPv6ProvisioningLossQuirkParcelable {
+  int detectionCount;
+  long quirkExpiry;
+}
diff --git a/common/networkstackclient/src/android/net/ipmemorystore/NetworkAttributes.java b/common/networkstackclient/src/android/net/ipmemorystore/NetworkAttributes.java
index 2e444fe..5be209e 100644
--- a/common/networkstackclient/src/android/net/ipmemorystore/NetworkAttributes.java
+++ b/common/networkstackclient/src/android/net/ipmemorystore/NetworkAttributes.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.quirks.IPv6ProvisioningLossQuirk;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -83,6 +84,13 @@
     public final Integer mtu;
     private static final float WEIGHT_MTU = 50.0f;
 
+    // IPv6 provisioning quirk info about this network, if applicable.
+    @Nullable
+    public final IPv6ProvisioningLossQuirk ipv6ProvLossQuirk;
+    // quirk information doesn't imply any correlation between "the same quirk detection count and
+    // expiry" and "the same L3 network".
+    private static final float WEIGHT_V6PROVLOSSQUIRK = 0.0f;
+
     // The sum of all weights in this class. Tests ensure that this stays equal to the total of
     // all weights.
     /** @hide */
@@ -91,7 +99,8 @@
             + WEIGHT_ASSIGNEDV4ADDREXPIRY
             + WEIGHT_CLUSTER
             + WEIGHT_DNSADDRESSES
-            + WEIGHT_MTU;
+            + WEIGHT_MTU
+            + WEIGHT_V6PROVLOSSQUIRK;
 
     /** @hide */
     @VisibleForTesting
@@ -100,7 +109,8 @@
             @Nullable final Long assignedV4AddressExpiry,
             @Nullable final String cluster,
             @Nullable final List<InetAddress> dnsAddresses,
-            @Nullable final Integer mtu) {
+            @Nullable final Integer mtu,
+            @Nullable final IPv6ProvisioningLossQuirk ipv6ProvLossQuirk) {
         if (mtu != null && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
         if (assignedV4AddressExpiry != null && assignedV4AddressExpiry <= 0) {
             throw new IllegalArgumentException("lease expiry can't be negative or zero");
@@ -111,6 +121,7 @@
         this.dnsAddresses = null == dnsAddresses ? null :
                 Collections.unmodifiableList(new ArrayList<>(dnsAddresses));
         this.mtu = mtu;
+        this.ipv6ProvLossQuirk = ipv6ProvLossQuirk;
     }
 
     @VisibleForTesting
@@ -122,7 +133,8 @@
                         ? parcelable.assignedV4AddressExpiry : null,
                 parcelable.cluster,
                 blobArrayToInetAddressList(parcelable.dnsAddresses),
-                parcelable.mtu >= 0 ? parcelable.mtu : null);
+                parcelable.mtu >= 0 ? parcelable.mtu : null,
+                IPv6ProvisioningLossQuirk.fromStableParcelable(parcelable.ipv6ProvLossQuirk));
     }
 
     @Nullable
@@ -171,6 +183,8 @@
         parcelable.cluster = cluster;
         parcelable.dnsAddresses = inetAddressListToBlobArray(dnsAddresses);
         parcelable.mtu = (null == mtu) ? -1 : mtu;
+        parcelable.ipv6ProvLossQuirk =
+                (null == ipv6ProvLossQuirk) ? null : ipv6ProvLossQuirk.toStableParcelable();
         return parcelable;
     }
 
@@ -184,13 +198,16 @@
 
     /** @hide */
     public float getNetworkGroupSamenessConfidence(@NonNull final NetworkAttributes o) {
+        // TODO: Remove the useless comparison for members which are associated with 0 weight.
         final float samenessScore =
                 samenessContribution(WEIGHT_ASSIGNEDV4ADDR, assignedV4Address, o.assignedV4Address)
                 + samenessContribution(WEIGHT_ASSIGNEDV4ADDREXPIRY, assignedV4AddressExpiry,
                       o.assignedV4AddressExpiry)
                 + samenessContribution(WEIGHT_CLUSTER, cluster, o.cluster)
                 + samenessContribution(WEIGHT_DNSADDRESSES, dnsAddresses, o.dnsAddresses)
-                + samenessContribution(WEIGHT_MTU, mtu, o.mtu);
+                + samenessContribution(WEIGHT_MTU, mtu, o.mtu)
+                + samenessContribution(WEIGHT_V6PROVLOSSQUIRK, ipv6ProvLossQuirk,
+                      o.ipv6ProvLossQuirk);
         // The minimum is 0, the max is TOTAL_WEIGHT and should be represented by 1.0, and
         // TOTAL_WEIGHT_CUTOFF should represent 0.5, but there is no requirement that
         // TOTAL_WEIGHT_CUTOFF would be half of TOTAL_WEIGHT (indeed, it should not be).
@@ -216,6 +233,8 @@
         private List<InetAddress> mDnsAddresses;
         @Nullable
         private Integer mMtu;
+        @Nullable
+        private IPv6ProvisioningLossQuirk mIpv6ProvLossQuirk;
 
         /**
          * Constructs a new Builder.
@@ -231,6 +250,7 @@
             mCluster = attributes.cluster;
             mDnsAddresses = new ArrayList<>(attributes.dnsAddresses);
             mMtu = attributes.mtu;
+            mIpv6ProvLossQuirk = attributes.ipv6ProvLossQuirk;
         }
 
         /**
@@ -298,19 +318,30 @@
         }
 
         /**
+         * Set the IPv6 Provisioning Loss Quirk information.
+         * @param quirk The IPv6 Provisioning Loss Quirk.
+         * @return This builder.
+         */
+        public Builder setIpv6ProvLossQuirk(@Nullable final IPv6ProvisioningLossQuirk quirk) {
+            mIpv6ProvLossQuirk = quirk;
+            return this;
+        }
+
+        /**
          * Return the built NetworkAttributes object.
          * @return The built NetworkAttributes object.
          */
         public NetworkAttributes build() {
             return new NetworkAttributes(mAssignedAddress, mAssignedAddressExpiry,
-                    mCluster, mDnsAddresses, mMtu);
+                    mCluster, mDnsAddresses, mMtu, mIpv6ProvLossQuirk);
         }
     }
 
     /** @hide */
     public boolean isEmpty() {
         return (null == assignedV4Address) && (null == assignedV4AddressExpiry)
-                && (null == cluster) && (null == dnsAddresses) && (null == mtu);
+                && (null == cluster) && (null == dnsAddresses) && (null == mtu)
+                && (null == ipv6ProvLossQuirk);
     }
 
     @Override
@@ -321,13 +352,14 @@
                 && Objects.equals(assignedV4AddressExpiry, other.assignedV4AddressExpiry)
                 && Objects.equals(cluster, other.cluster)
                 && Objects.equals(dnsAddresses, other.dnsAddresses)
-                && Objects.equals(mtu, other.mtu);
+                && Objects.equals(mtu, other.mtu)
+                && Objects.equals(ipv6ProvLossQuirk, other.ipv6ProvLossQuirk);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(assignedV4Address, assignedV4AddressExpiry,
-                cluster, dnsAddresses, mtu);
+                cluster, dnsAddresses, mtu, ipv6ProvLossQuirk);
     }
 
     /** Pretty print */
@@ -374,6 +406,14 @@
             nullFields.add("mtu");
         }
 
+        if (null != ipv6ProvLossQuirk) {
+            resultJoiner.add("ipv6ProvLossQuirk : [");
+            resultJoiner.add(ipv6ProvLossQuirk.toString());
+            resultJoiner.add("]");
+        } else {
+            nullFields.add("ipv6ProvLossQuirk");
+        }
+
         if (!nullFields.isEmpty()) {
             resultJoiner.add("; Null fields : [");
             for (final String field : nullFields) {
diff --git a/common/networkstackclient/src/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/common/networkstackclient/src/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
index b710427..f49fe51 100644
--- a/common/networkstackclient/src/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
+++ b/common/networkstackclient/src/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
@@ -19,6 +19,7 @@
 // Blob[] is used to represent an array of byte[], as structured AIDL does not support arrays
 // of arrays.
 import android.net.ipmemorystore.Blob;
+import android.net.quirks.IPv6ProvisioningLossQuirkParcelable;
 
 /**
  * An object to represent attributes of a single L2 network entry.
@@ -34,4 +35,5 @@
     String cluster;
     Blob[] dnsAddresses;
     int mtu;
+    @nullable IPv6ProvisioningLossQuirkParcelable ipv6ProvLossQuirk;
 }
diff --git a/common/networkstackclient/src/android/net/quirks/IPv6ProvisioningLossQuirk.java b/common/networkstackclient/src/android/net/quirks/IPv6ProvisioningLossQuirk.java
new file mode 100644
index 0000000..36bbac8
--- /dev/null
+++ b/common/networkstackclient/src/android/net/quirks/IPv6ProvisioningLossQuirk.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.quirks;
+
+import android.annotation.Nullable;
+
+import java.util.Objects;
+
+/**
+ * An object representing the quirk information results in the IPv6 provisioning loss on the given
+ * network. Parcels as a IPv6ProvisioningLossQuirkParcelable object.
+ * @hide
+ */
+public final class IPv6ProvisioningLossQuirk {
+    public final int mDetectionCount;
+    public final long mQuirkExpiry;
+
+    /**
+     * Create an instance of {@link IPv6ProvisioningLossQuirk} with the specified members.
+     */
+    public IPv6ProvisioningLossQuirk(final int count, final long expiry) {
+        mDetectionCount = count;
+        mQuirkExpiry = expiry;
+    }
+
+    /**
+     * Convert IPv6ProvisioningLossQuirk to a {@link IPv6ProvisioningLossQuirkParcelable}.
+     */
+    public IPv6ProvisioningLossQuirkParcelable toStableParcelable() {
+        final IPv6ProvisioningLossQuirkParcelable p = new IPv6ProvisioningLossQuirkParcelable();
+        p.detectionCount = mDetectionCount;
+        p.quirkExpiry = mQuirkExpiry;
+        return p;
+    }
+
+    /**
+     * Create an instance of {@link IPv6ProvisioningLossQuirk} based on the contents of the
+     * specified {@link IPv6ProvisioningLossQuirkParcelable}.
+     */
+    public static IPv6ProvisioningLossQuirk fromStableParcelable(
+            @Nullable final IPv6ProvisioningLossQuirkParcelable p) {
+        if (p == null) return null;
+        return new IPv6ProvisioningLossQuirk(p.detectionCount, p.quirkExpiry);
+    }
+
+    @Override
+    public boolean equals(@Nullable final Object obj) {
+        if (null == obj || getClass() != obj.getClass()) return false;
+        final IPv6ProvisioningLossQuirk other = (IPv6ProvisioningLossQuirk) obj;
+        return mDetectionCount == other.mDetectionCount && mQuirkExpiry == other.mQuirkExpiry;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDetectionCount, mQuirkExpiry);
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer str = new StringBuffer();
+        str.append("detection count: ").append(mDetectionCount);
+        str.append(", quirk expiry: ").append(mQuirkExpiry);
+        return str.toString();
+    }
+}
diff --git a/common/networkstackclient/src/android/net/quirks/IPv6ProvisioningLossQuirkParcelable.aidl b/common/networkstackclient/src/android/net/quirks/IPv6ProvisioningLossQuirkParcelable.aidl
new file mode 100644
index 0000000..7a2f4bb
--- /dev/null
+++ b/common/networkstackclient/src/android/net/quirks/IPv6ProvisioningLossQuirkParcelable.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.quirks;
+
+parcelable IPv6ProvisioningLossQuirkParcelable {
+    int detectionCount;
+    long quirkExpiry;
+}
diff --git a/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
index c0bdc4c..73edcc7 100644
--- a/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -506,7 +506,7 @@
         // Verify that this test does not miss any new field added later.
         // If any field is added to NetworkAttributes it must be tested here for storing
         // and retrieving.
-        assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+        assertEquals(6, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
                 .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
     }