Merge changes Idce2024f,I7e009073,I29fe506c,I37b2a0b8,Iccb4e3ec into main am: 49967eab24

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

Change-Id: I299acf86d38e4710be738eb36a0d336d8bd56846
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/staticlibs/device/com/android/net/module/util/Struct.java b/staticlibs/device/com/android/net/module/util/Struct.java
index b638a46..dc0d19b 100644
--- a/staticlibs/device/com/android/net/module/util/Struct.java
+++ b/staticlibs/device/com/android/net/module/util/Struct.java
@@ -146,6 +146,14 @@
         int arraysize() default 0;
     }
 
+    /**
+     * Indicates that this field contains a computed value and is ignored for the purposes of Struct
+     * parsing.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.FIELD)
+    public @interface Computed {}
+
     private static class FieldInfo {
         @NonNull
         public final Field annotation;
@@ -533,6 +541,7 @@
         final FieldInfo[] annotationFields = new FieldInfo[getAnnotationFieldCount(clazz)];
         for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {
             if (Modifier.isStatic(field.getModifiers())) continue;
+            if (field.getAnnotation(Computed.class) != null) continue;
 
             final Field annotation = field.getAnnotation(Field.class);
             if (annotation == null) {
diff --git a/staticlibs/device/com/android/net/module/util/structs/IaPrefixOption.java b/staticlibs/device/com/android/net/module/util/structs/IaPrefixOption.java
index e9c39e4..ecdd440 100644
--- a/staticlibs/device/com/android/net/module/util/structs/IaPrefixOption.java
+++ b/staticlibs/device/com/android/net/module/util/structs/IaPrefixOption.java
@@ -18,12 +18,17 @@
 
 import static com.android.net.module.util.NetworkStackConstants.DHCP6_OPTION_IAPREFIX;
 
+import android.net.IpPrefix;
 import android.util.Log;
 
 import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Computed;
 import com.android.net.module.util.Struct.Field;
 import com.android.net.module.util.Struct.Type;
 
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
@@ -70,7 +75,11 @@
     @Field(order = 5, type = Type.ByteArray, arraysize = 16)
     public final byte[] prefix;
 
-    public IaPrefixOption(final short code, final short length, final long preferred,
+    @Computed
+    private final IpPrefix mIpPrefix;
+
+    // Constructor used by Struct.parse()
+    protected IaPrefixOption(final short code, final short length, final long preferred,
             final long valid, final byte prefixLen, final byte[] prefix) {
         this.code = code;
         this.length = length;
@@ -78,30 +87,43 @@
         this.valid = valid;
         this.prefixLen = prefixLen;
         this.prefix = prefix.clone();
+
+        try {
+            final Inet6Address addr = (Inet6Address) InetAddress.getByAddress(prefix);
+            mIpPrefix = new IpPrefix(addr, prefixLen);
+        } catch (UnknownHostException | ClassCastException e) {
+            // UnknownHostException should never happen unless prefix is null.
+            // ClassCastException can occur when prefix is an IPv6 mapped IPv4 address.
+            // Both scenarios should throw an exception in the context of Struct#parse().
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public IaPrefixOption(final short length, final long preferred, final long valid,
+            final byte prefixLen, final byte[] prefix) {
+        this((byte) DHCP6_OPTION_IAPREFIX, length, preferred, valid, prefixLen, prefix);
     }
 
     /**
      * Check whether or not IA Prefix option in IA_PD option is valid per RFC8415#section-21.22.
+     *
+     * Note: an expired prefix can still be valid.
      */
-    public boolean isValid(int t2) {
-        if (preferred < 0 || valid < 0) {
-            Log.w(TAG, "IA_PD option with invalid lifetime, preferred lifetime " + preferred
-                    + ", valid lifetime " + valid);
+    public boolean isValid() {
+        if (preferred < 0) {
+            Log.w(TAG, "Invalid preferred lifetime: " + this);
+            return false;
+        }
+        if (valid < 0) {
+            Log.w(TAG, "Invalid valid lifetime: " + this);
             return false;
         }
         if (preferred > valid) {
-            Log.w(TAG, "IA_PD option with preferred lifetime " + preferred
-                    + " greater than valid lifetime " + valid);
+            Log.w(TAG, "Invalid lifetime. Preferred lifetime > valid lifetime: " + this);
             return false;
         }
         if (prefixLen > 64) {
-            Log.w(TAG, "IA_PD option with prefix length " + prefixLen
-                    + " longer than 64");
-            return false;
-        }
-        // Either preferred lifetime or t2 might be 0 which is valid, then ignore it.
-        if (preferred != 0 && t2 != 0 && preferred < t2) {
-            Log.w(TAG, "preferred lifetime " + preferred + " is smaller than T2 " + t2);
+            Log.w(TAG, "Invalid prefix length: " + this);
             return false;
         }
         return true;
@@ -119,8 +141,14 @@
      */
     public static ByteBuffer build(final short length, final long preferred, final long valid,
             final byte prefixLen, final byte[] prefix) {
-        final IaPrefixOption option = new IaPrefixOption((byte) DHCP6_OPTION_IAPREFIX,
+        final IaPrefixOption option = new IaPrefixOption(
                 length /* 25 + IAPrefix options length */, preferred, valid, prefixLen, prefix);
         return ByteBuffer.wrap(option.writeToBytes(ByteOrder.BIG_ENDIAN));
     }
+
+    @Override
+    public String toString() {
+        return "IA Prefix, length " + length + ": " + mIpPrefix + ", pref " + preferred + ", valid "
+                + valid;
+    }
 }