Fix serialization of class fields with same names.

Dex allows for fields to share a name if they have
different types. openJdk serialization code was not
handling this case properly.

Bug: 29721023
Change-Id: I3b8ea027f0af829e9f158d4bcb11a2052e34c10f
(cherry picked from commit 0885fcbbbf2fdc46ffe847aca9d3c339f4c7bb41)
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectInputStream2Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectInputStream2Test.java
index af5fce5..e81224a 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectInputStream2Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectInputStream2Test.java
@@ -17,8 +17,10 @@
 
 package org.apache.harmony.tests.java.io;
 
+import dalvik.system.DexFile;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InvalidClassException;
@@ -27,6 +29,9 @@
 import java.io.ObjectStreamClass;
 import java.io.ObjectStreamException;
 import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 
 import junit.framework.TestCase;
@@ -210,4 +215,54 @@
             // Excpected
         }
     }
+
+    // http://b/29721023
+    public void test_sameName() throws Exception {
+        // Load class from dex, it's not possible to create a class with same-named
+        // fields in java (but it's allowed in dex).
+        File sameFieldNames = File.createTempFile("sameFieldNames", ".dex");
+        InputStream dexIs = this.getClass().getClassLoader().
+            getResourceAsStream("tests/api/java/io/sameFieldNames.dex");
+        assertNotNull(dexIs);
+
+        Class<?> clazz = null;
+
+        // Get the class object
+        try {
+            Files.copy(dexIs, sameFieldNames.toPath(), StandardCopyOption.REPLACE_EXISTING);
+            DexFile dexFile = new DexFile(sameFieldNames);
+            clazz = dexFile.loadClass("sameFieldNames", getClass().getClassLoader());
+            dexFile.close();
+        } finally {
+            if (sameFieldNames.exists()) {
+                sameFieldNames.delete();
+            }
+        }
+
+        // Create class instance, fill it with content
+        Object o1 = clazz.getConstructor().newInstance();
+        int v = 123;
+        for(Field f : clazz.getFields()) {
+            if (f.getType() == Integer.class) {
+                f.set(o1, new Integer(v++));
+            } else if (f.getType() == Long.class) {
+                f.set(o1, new Long(v++));
+            }
+        }
+
+        // Serialize and deserialize
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(o1);
+        oos.close();
+        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
+                baos.toByteArray()));
+        Object o2 = ois.readObject();
+        ois.close();
+
+        // Compare content
+        for(Field f : clazz.getFields()) {
+            assertEquals(f.get(o1), f.get(o2));
+        }
+    }
 }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
index 7ae4617..948059b 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
@@ -17,16 +17,22 @@
 
 package org.apache.harmony.tests.java.io;
 
-import junit.framework.TestCase;
+import dalvik.system.DexFile;
 import java.io.Externalizable;
+import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 import java.io.ObjectStreamClass;
 import java.io.ObjectStreamField;
 import java.io.Serializable;
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import junit.framework.TestCase;
 
 public class ObjectStreamClassTest extends TestCase {
 
@@ -294,4 +300,27 @@
                     hasStaticInitializer.invoke(null, NoClinitChildWithNoClinitParent.class,
                                                 true /* checkSuperclass */));
     }
+
+    // http://b/29721023
+    public void testClassWithSameFieldName() throws Exception {
+        // Load class from dex, it's not possible to create a class with same-named
+        // fields in java (but it's allowed in dex).
+        File sameFieldNames = File.createTempFile("sameFieldNames", ".dex");
+        InputStream dexIs = this.getClass().getClassLoader().
+            getResourceAsStream("tests/api/java/io/sameFieldNames.dex");
+        assertNotNull(dexIs);
+
+        try {
+            Files.copy(dexIs, sameFieldNames.toPath(), StandardCopyOption.REPLACE_EXISTING);
+            DexFile dexFile = new DexFile(sameFieldNames);
+            Class<?> clazz = dexFile.loadClass("sameFieldNames", getClass().getClassLoader());
+            ObjectStreamClass osc = ObjectStreamClass.lookup(clazz);
+            assertEquals(4, osc.getFields().length);
+            dexFile.close();
+        } finally {
+            if (sameFieldNames.exists()) {
+                sameFieldNames.delete();
+            }
+        }
+    }
 }
diff --git a/luni/src/test/resources/tests/api/java/io/sameFieldNames.dex b/luni/src/test/resources/tests/api/java/io/sameFieldNames.dex
new file mode 100644
index 0000000..ada4935
--- /dev/null
+++ b/luni/src/test/resources/tests/api/java/io/sameFieldNames.dex
Binary files differ
diff --git a/luni/src/test/resources/tests/api/java/io/sameFieldNames.smali b/luni/src/test/resources/tests/api/java/io/sameFieldNames.smali
new file mode 100644
index 0000000..657b965
--- /dev/null
+++ b/luni/src/test/resources/tests/api/java/io/sameFieldNames.smali
@@ -0,0 +1,17 @@
+# Source for sameFieldNames.dex
+.class public LsameFieldNames;
+.super Ljava/lang/Object;
+.implements Ljava/io/Serializable;
+
+# Test multiple fields with the same name and different types.
+# (Invalid in Java language but valid in bytecode.)
+.field public a:J
+.field public a:I
+.field public a:Ljava/lang/Integer;
+.field public a:Ljava/lang/Long;
+
+.method public constructor <init>()V
+    .registers 2
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+    return-void
+.end method
diff --git a/ojluni/src/main/java/java/io/ObjectStreamClass.java b/ojluni/src/main/java/java/io/ObjectStreamClass.java
index a157f4d..ffbde98 100755
--- a/ojluni/src/main/java/java/io/ObjectStreamClass.java
+++ b/ojluni/src/main/java/java/io/ObjectStreamClass.java
@@ -2246,13 +2246,9 @@
             ObjectStreamField f = fields[i], m = null;
             for (int j = 0; j < localFields.length; j++) {
                 ObjectStreamField lf = localFields[j];
-                if (f.getName().equals(lf.getName())) {
-                    if ((f.isPrimitive() || lf.isPrimitive()) &&
-                        f.getTypeCode() != lf.getTypeCode())
-                    {
-                        throw new InvalidClassException(localDesc.name,
-                            "incompatible types for field " + f.getName());
-                    }
+                // Android-changed: We can have fields with a same name and a different type.
+                if (f.getName().equals(lf.getName()) &&
+                    f.getSignature().equals(lf.getSignature())) {
                     if (lf.getField() != null) {
                         m = new ObjectStreamField(
                             lf.getField(), lf.isUnshared(), false);