Emit equals and hashcode for auto-generated Java code.

Test: hidl_test_java
Bug: 32834072

Change-Id: I660852b7d76f79d41b93c9986e4c1188c02ba4a6
diff --git a/CompoundType.cpp b/CompoundType.cpp
index 5f3dd5a..6527f86 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -16,6 +16,7 @@
 
 #include "CompoundType.h"
 
+#include "ArrayType.h"
 #include "VectorType.h"
 #include <hidl-util/Formatter.h>
 #include <android-base/logging.h>
@@ -489,6 +490,50 @@
         out << "\n";
     }
 
+    ////////////////////////////////////////////////////////////////////////////
+
+    if (canCheckEquality()) {
+        out << "public final boolean equals(" << localName() << " other) ";
+        out.block([&] {
+            for (const auto &field : *mFields) {
+                std::string condition = field->type().isScalar()
+                    ? "this." + field->name() + " != other." + field->name()
+                    : ("!java.util.Objects.deepEquals(this." + field->name()
+                            + ", other." + field->name() + ")");
+                out.sIf(condition, [&] {
+                    out << "return false;\n";
+                }).endl();
+            }
+            out << "return true;\n";
+        }).endl().endl();
+
+        out << "public final int hashCode() ";
+        out.block([&] {
+            out << "return java.util.Objects.hash(";
+            bool first = true;
+            for (const auto &field : *mFields) {
+                if (!first) {
+                    out << ", ";
+                }
+                first = false;
+                if (field->type().isArray()) {
+                    const ArrayType &type = static_cast<const ArrayType &>(field->type());
+                    if (type.countDimensions() == 1 &&
+                        type.getElementType()->resolveToScalarType() != nullptr) {
+                        out << "java.util.Arrays.hashCode(this." << field->name() << ")";
+                    } else {
+                        out << "java.util.Arrays.deepHashCode(this." << field->name() << ")";
+                    }
+                } else {
+                    out << "this." << field->name();
+                }
+            }
+            out << ");\n";
+        }).endl().endl();
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+
     out << "public final void readFromParcel(android.os.HwParcel parcel) {\n";
     out.indent();
     out << "android.os.HwBlob blob = parcel.readBuffer();\n";
diff --git a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
index e80584a..0050045 100644
--- a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
+++ b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
@@ -736,6 +736,20 @@
         System.gc();
         proxy.iAmFreeNow();
 
+        {
+            IBaz.T t1 = new IBaz.T();
+            IBaz.T t2 = new IBaz.T();
+            for (int i = 0; i < 5; i++) {
+                for (int j = 0; j < 3; j++) {
+                    t1.matrix5x3[i][j] = t2.matrix5x3[i][j] = (i + 1) * (j + 1);
+                }
+            }
+            ExpectTrue(t1.equals(t2));
+            ExpectTrue(t1.hashCode() == t2.hashCode());
+            t2.matrix5x3[4][2] = -60;
+            ExpectTrue(!t1.equals(t2));
+        }
+
         // --- DEATH RECIPIENT TESTING ---
         // This must always be done last, since it will kill the native server process
         HidlDeathRecipient recipient1 = new HidlDeathRecipient();
@@ -755,6 +769,7 @@
         ExpectTrue(!recipient2.waitUntilServiceDied(2000 /*timeoutMillis*/));
         ExpectTrue(recipient1.cookieMatches(cookie1));
         Log.d(TAG, "OK, exiting");
+
     }
 
     class Baz extends IBaz.Stub {