Merge "-Landroidbp-impl shouldn't require libhwbinder"
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 7753031..b47ba3b 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -318,6 +318,17 @@
out << "}\n\n";
}
+void ArrayType::emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const {
+ out << streamName << ".append(java.util.Arrays."
+ << (countDimensions() > 1 ? "deepToString" : "toString")
+ << "("
+ << name << "));\n";
+}
+
+
bool ArrayType::needsEmbeddedReadWrite() const {
return mElementType->needsEmbeddedReadWrite();
}
diff --git a/ArrayType.h b/ArrayType.h
index e17df10..a0c2a08 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -95,6 +95,11 @@
const std::string &parentName,
const std::string &offsetText) const override;
+ void emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const override;
+
bool needsEmbeddedReadWrite() const override;
bool needsResolveReferences() const override;
bool resultNeedsDeref() const override;
diff --git a/CompoundType.cpp b/CompoundType.cpp
index 2dfc2bc..dcd426a 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -565,8 +565,19 @@
////////////////////////////////////////////////////////////////////////////
if (canCheckEquality()) {
- out << "public final boolean equals(" << localName() << " other) ";
+ out << "@Override\npublic final boolean equals(Object otherObject) ";
out.block([&] {
+ out.sIf("this == otherObject", [&] {
+ out << "return true;\n";
+ }).endl();
+ out.sIf("otherObject == null", [&] {
+ out << "return false;\n";
+ }).endl();
+ // Class is final, so we only need to check instanceof, not getClass
+ out.sIf("!(otherObject instanceof " + fullJavaName() + ")", [&] {
+ out << "return false;\n";
+ }).endl();
+ out << fullJavaName() << " other = (" << fullJavaName() << ")otherObject;\n";
for (const auto &field : *mFields) {
std::string condition = field->type().isScalar()
? "this." + field->name() + " != other." + field->name()
@@ -579,33 +590,52 @@
out << "return true;\n";
}).endl().endl();
- out << "public final int hashCode() ";
+ out << "@Override\npublic 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() << ")";
+ out << "return java.util.Objects.hash(\n";
+ out.indent(2, [&] {
+ bool first = true;
+ for (const auto &field : *mFields) {
+ if (!first) {
+ out << ", \n";
}
- } else {
- out << "this." << field->name();
+ 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 << "@Override\npublic final String toString() ";
+ out.block([&] {
+ out << "java.lang.StringBuilder builder = new java.lang.StringBuilder();\n"
+ << "builder.append(\"{\");\n";
+ for (const auto &field : *mFields) {
+ out << "builder.append(\"";
+ if (field != *(mFields->begin())) {
+ out << ", ";
+ }
+ out << "." << field->name() << " = \");\n";
+ field->type().emitJavaDump(out, "builder", "this." + field->name());
+ }
+ out << "builder.append(\"}\");\nreturn builder.toString();\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/EnumType.cpp b/EnumType.cpp
index 87cc19b..0ef2590 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -32,6 +32,8 @@
: Scope(localName, location),
mValues(),
mStorageType(storageType) {
+ mBitfieldType = new BitFieldType();
+ mBitfieldType->setElementType(this);
}
const Type *EnumType::storageType() const {
@@ -101,6 +103,10 @@
return "TYPE_ENUM";
}
+BitFieldType *EnumType::getBitfieldType() const {
+ return mBitfieldType;
+}
+
LocalIdentifier *EnumType::lookupIdentifier(const std::string &name) const {
std::vector<const EnumType *> chain;
getTypeChain(&chain);
@@ -267,6 +273,7 @@
emitBitFieldBitwiseAssignmentOperator(out, "|");
emitBitFieldBitwiseAssignmentOperator(out, "&");
+ // toString for bitfields, equivalent to dumpBitfield in Java
out << "template<typename>\n"
<< "std::string toString("
<< resolveToScalarType()->getCppArgumentType()
@@ -276,14 +283,10 @@
<< resolveToScalarType()->getCppArgumentType()
<< " o);\n\n";
- out << "inline std::string toString("
+ // toString for enum itself
+ out << "std::string toString("
<< getCppArgumentType()
- << " o) ";
-
- out.block([&] {
- out << "return toString<" << getCppStackType() << ">("
- << "static_cast<" << resolveToScalarType()->getCppStackType() << ">(o));\n";
- }).endl().endl();
+ << " o);\n\n";
return OK;
}
@@ -295,20 +298,30 @@
out << "template<>\n"
<< "std::string toString<" << getCppStackType() << ">("
- << resolveToScalarType()->getCppArgumentType()
+ << scalarType->getCppArgumentType()
<< " o) ";
out.block([&] {
// include toHexString for scalar types
out << "using ::android::hardware::details::toHexString;\n"
- << "std::string os;\n";
- out << "bool first = true;\n";
+ << "std::string os;\n"
+ << getBitfieldType()->getCppStackType() << " flipped = 0;\n"
+ << "bool first = true;\n";
for (EnumValue *value : values()) {
- out.sIf("o & " + fullName() + "::" + value->name(), [&] {
- out << "os += (first ? \"\" : \" | \"); "
+ std::string valueName = fullName() + "::" + value->name();
+ out.sIf("(o & " + valueName + ")" +
+ " == static_cast<" + scalarType->getCppStackType() +
+ ">(" + valueName + ")", [&] {
+ out << "os += (first ? \"\" : \" | \");\n"
+ << "os += \"" << value->name() << "\";\n"
<< "first = false;\n"
- << "os += \"" << value->name() << "\";\n";
+ << "flipped |= " << valueName << ";\n";
}).endl();
}
+ // put remaining bits
+ out.sIf("o != flipped", [&] {
+ out << "os += (first ? \"\" : \" | \");\n";
+ scalarType->emitHexDump(out, "os", "o & (~flipped)");
+ });
out << "os += \" (\";\n";
scalarType->emitHexDump(out, "os", "o");
out << "os += \")\";\n";
@@ -316,14 +329,33 @@
out << "return os;\n";
}).endl().endl();
+ out << "std::string toString("
+ << getCppArgumentType()
+ << " o) ";
+
+ out.block([&] {
+ out << "using ::android::hardware::details::toHexString;\n";
+ for (EnumValue *value : values()) {
+ out.sIf("o == " + fullName() + "::" + value->name(), [&] {
+ out << "return \"" << value->name() << "\";\n";
+ }).endl();
+ }
+ out << "std::string os;\n";
+ scalarType->emitHexDump(out, "os",
+ "static_cast<" + scalarType->getCppStackType() + ">(o)");
+ out << "return os;\n";
+ }).endl().endl();
+
return OK;
}
-status_t EnumType::emitJavaTypeDeclarations(Formatter &out, bool) const {
+status_t EnumType::emitJavaTypeDeclarations(Formatter &out, bool atTopLevel) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != NULL);
- out << "public final class "
+ out << "public "
+ << (atTopLevel ? "" : "static ")
+ << "final class "
<< localName()
<< " {\n";
@@ -361,6 +393,42 @@
}
}
+ out << "public static final String toString("
+ << typeName << " o) ";
+ out.block([&] {
+ for (EnumValue *value : values()) {
+ out.sIf("o == " + value->name(), [&] {
+ out << "return \"" << value->name() << "\";\n";
+ }).endl();
+ }
+ out << "return \"0x\" + ";
+ scalarType->emitConvertToJavaHexString(out, "o");
+ out << ";\n";
+ }).endl();
+
+ auto bitfieldType = getBitfieldType()->getJavaType(false /* forInitializer */);
+ auto bitfieldWrapperType = getBitfieldType()->getJavaWrapperType();
+ out << "\n"
+ << "public static final String dumpBitfield("
+ << bitfieldType << " o) ";
+ out.block([&] {
+ out << "java.util.ArrayList<String> list = new java.util.ArrayList<>();\n";
+ out << bitfieldType << " flipped = 0;\n";
+ for (EnumValue *value : values()) {
+ out.sIf("(o & " + value->name() + ") == " + value->name(), [&] {
+ out << "list.add(\"" << value->name() << "\");\n";
+ out << "flipped |= " << value->name() << ";\n";
+ }).endl();
+ }
+ // put remaining bits
+ out.sIf("o != flipped", [&] {
+ out << "list.add(\"0x\" + ";
+ scalarType->emitConvertToJavaHexString(out, "o & (~flipped)");
+ out << ");\n";
+ }).endl();
+ out << "return String.join(\" | \", list);\n";
+ }).endl().endl();
+
out.unindent();
out << "};\n\n";
@@ -411,6 +479,14 @@
return OK;
}
+void EnumType::emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const {
+ out << streamName << ".append(" << fqName().javaName() << ".toString("
+ << name << "));\n";
+}
+
void EnumType::getTypeChain(std::vector<const EnumType *> *out) const {
out->clear();
const EnumType *type = this;
@@ -682,6 +758,10 @@
return resolveToScalarType()->isElidableType();
}
+bool BitFieldType::canCheckEquality() const {
+ return resolveToScalarType()->canCheckEquality();
+}
+
status_t BitFieldType::emitVtsAttributeType(Formatter &out) const {
out << "type: " << getVtsType() << "\n";
out << "scalar_type: \""
@@ -713,6 +793,11 @@
true /* needsCast */);
}
+EnumType *BitFieldType::getEnumType() const {
+ CHECK(mElementType->isEnum());
+ return static_cast<EnumType *>(mElementType);
+}
+
// a bitfield maps to the underlying scalar type in C++, so operator<< is
// already defined. We can still emit useful information if the bitfield is
// in a struct / union by overriding emitDump as below.
@@ -720,13 +805,19 @@
Formatter &out,
const std::string &streamName,
const std::string &name) const {
- CHECK(mElementType->isEnum());
- const EnumType *enumType = static_cast<EnumType *>(mElementType);
- out << streamName << " += "<< enumType->fqName().cppNamespace()
- << "::toString<" << enumType->getCppStackType()
+ out << streamName << " += "<< getEnumType()->fqName().cppNamespace()
+ << "::toString<" << getEnumType()->getCppStackType()
<< ">(" << name << ");\n";
}
+void BitFieldType::emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const {
+ out << streamName << ".append(" << getEnumType()->fqName().javaName() << ".dumpBitfield("
+ << name << "));\n";
+}
+
void BitFieldType::emitJavaFieldReaderWriter(
Formatter &out,
size_t depth,
diff --git a/EnumType.h b/EnumType.h
index db5efdc..b853054 100644
--- a/EnumType.h
+++ b/EnumType.h
@@ -26,6 +26,7 @@
namespace android {
struct EnumValue;
+struct BitFieldType;
struct EnumType : public Scope {
EnumType(const char *localName,
@@ -56,6 +57,9 @@
std::string getVtsType() const override;
+ // Return the type that corresponds to bitfield<T>.
+ BitFieldType *getBitfieldType() const;
+
void emitReaderWriter(
Formatter &out,
const std::string &name,
@@ -83,6 +87,11 @@
status_t emitVtsTypeDeclarations(Formatter &out) const override;
status_t emitVtsAttributeType(Formatter &out) const override;
+ void emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const override;
+
void getAlignmentAndSize(size_t *align, size_t *size) const override;
void appendToExportedTypesVector(
@@ -106,6 +115,7 @@
std::vector<EnumValue *> mValues;
Type *mStorageType;
+ BitFieldType *mBitfieldType;
DISALLOW_COPY_AND_ASSIGN(EnumType);
};
@@ -144,6 +154,8 @@
bool isElidableType() const override;
+ bool canCheckEquality() const override;
+
const ScalarType *resolveToScalarType() const override;
std::string getCppType(StorageMode mode,
@@ -157,6 +169,8 @@
std::string getVtsType() const override;
+ EnumType *getEnumType() const;
+
status_t emitVtsAttributeType(Formatter &out) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
@@ -174,6 +188,11 @@
const std::string &streamName,
const std::string &name) const override;
+ void emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const override;
+
void emitJavaFieldReaderWriter(
Formatter &out,
size_t depth,
diff --git a/ScalarType.cpp b/ScalarType.cpp
index 9b6c0c3..b8bd427 100644
--- a/ScalarType.cpp
+++ b/ScalarType.cpp
@@ -218,6 +218,40 @@
out << streamName << " += toHexString(" << name << ");\n";
}
+void ScalarType::emitConvertToJavaHexString(
+ Formatter &out,
+ const std::string &name) const {
+ switch(mKind) {
+ case KIND_BOOL: {
+ out << "((" << name << ") ? \"0x1\" : \"0x0\")";
+ break;
+ }
+ case KIND_INT8: // fallthrough
+ case KIND_UINT8: // fallthrough
+ case KIND_INT16: // fallthrough
+ case KIND_UINT16: {
+ // Because Byte and Short doesn't have toHexString, we have to use Integer.toHexString.
+ out << "Integer.toHexString(" << getJavaWrapperType() << ".toUnsignedInt(("
+ << getJavaType(false /* forInitializer */) << ")(" << name << ")))";
+ break;
+ }
+ case KIND_INT32: // fallthrough
+ case KIND_UINT32: // fallthrough
+ case KIND_INT64: // fallthrough
+ case KIND_UINT64: {
+ out << getJavaWrapperType() << ".toHexString(" << name << ")";
+ break;
+ }
+ case KIND_FLOAT: // fallthrough
+ case KIND_DOUBLE: // fallthrough
+ default: {
+ // no hex for floating point numbers.
+ out << name;
+ break;
+ }
+ }
+}
+
void ScalarType::emitJavaFieldReaderWriter(
Formatter &out,
size_t /* depth */,
diff --git a/ScalarType.h b/ScalarType.h
index 8b70d72..a8a5f68 100644
--- a/ScalarType.h
+++ b/ScalarType.h
@@ -85,6 +85,10 @@
const std::string &streamName,
const std::string &name) const;
+ void emitConvertToJavaHexString(
+ Formatter &out,
+ const std::string &name) const;
+
void emitJavaFieldReaderWriter(
Formatter &out,
size_t depth,
diff --git a/Type.cpp b/Type.cpp
index 71279c0..546d6ab 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -214,6 +214,13 @@
<< ");\n";
}
+void Type::emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const {
+ out << streamName << ".append(" << name << ");\n";
+}
+
bool Type::useParentInEmitResolveReferencesEmbedded() const {
return needsResolveReferences();
}
diff --git a/Type.h b/Type.h
index f926ed2..ce2a03a 100644
--- a/Type.h
+++ b/Type.h
@@ -158,6 +158,11 @@
const std::string &streamName,
const std::string &name) const;
+ virtual void emitJavaDump(
+ Formatter &out,
+ const std::string &streamName,
+ const std::string &name) const;
+
virtual bool useParentInEmitResolveReferencesEmbedded() const;
virtual bool useNameInEmitReaderWriterEmbedded(bool isReader) const;
diff --git a/generateJava.cpp b/generateJava.cpp
index 56ac2d6..3e8d493 100644
--- a/generateJava.cpp
+++ b/generateJava.cpp
@@ -303,6 +303,18 @@
out.unindent();
out << "}\n\n";
+
+ out << "@Override\npublic String toString() ";
+ out.block([&] {
+ out.sTry([&] {
+ out << "return this.interfaceDescriptor() + \"@Proxy\";\n";
+ }).sCatch("RemoteException ex", [&] {
+ out << "/* ignored; handled below. */\n";
+ }).endl();
+ out << "return \"[class or subclass of \" + "
+ << ifaceName << ".kInterfaceName + \"]@Proxy\";\n";
+ }).endl().endl();
+
const Interface *prevInterface = nullptr;
for (const auto &tuple : iface->allMethodsFromRoot()) {
const Method *method = tuple.method();
@@ -488,6 +500,11 @@
out.unindent();
out << "}\n\n";
+ out << "@Override\npublic String toString() ";
+ out.block([&] {
+ out << "return this.interfaceDescriptor() + \"@Stub\";\n";
+ }).endl().endl();
+
out << "@Override\n"
<< "public void onTransact("
<< "int _hidl_code, "
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 acafeae..4864371 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
@@ -25,6 +25,7 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Arrays;
public final class HidlTestJava {
private static final String TAG = "HidlTestJava";
@@ -367,6 +368,7 @@
return;
}
+ System.err.printf("Expected '%s', got '%s'\n", s, result);
Log.e(TAG, "Expected '" + s + "', got '" + result + "'");
throw new RuntimeException();
}
@@ -759,6 +761,36 @@
ExpectTrue(structs.size() == 5);
ExpectTrue(structs.get(1).matrices.size() == 6);
+ {
+ IBaz.Everything e = new IBaz.Everything();
+ Expect(e.toString(),
+ "{.number = 0, .anotherNumber = 0, .s = , " +
+ ".vs = [], .multidimArray = [[null, null], [null, null]], " +
+ ".sArray = [null, null, null], .anotherStruct = {.first = , .last = }, .bf = }");
+ e.s = "string!";
+ e.number = 127;
+ e.anotherNumber = 100;
+ e.vs.addAll(Arrays.asList("One", "Two", "Three"));
+ for (int i = 0; i < e.multidimArray.length; i++)
+ for (int j = 0; j < e.multidimArray[i].length; j++)
+ e.multidimArray[i][j] = Integer.toString(i) + Integer.toString(j);
+ e.bf = IBaz.BitField.VALL;
+ e.anotherStruct.first = "James";
+ e.anotherStruct.last = "Bond";
+ Expect(e.toString(),
+ "{.number = 127, .anotherNumber = 100, .s = string!, " +
+ ".vs = [One, Two, Three], .multidimArray = [[00, 01], [10, 11]], " +
+ ".sArray = [null, null, null], .anotherStruct = {.first = James, .last = Bond}, " +
+ ".bf = V0 | V1 | V2 | V3 | VALL}");
+ Expect(IBaz.BitField.toString(IBaz.BitField.VALL), "VALL");
+ Expect(IBaz.BitField.toString((byte)(IBaz.BitField.V0 | IBaz.BitField.V2)), "0x5");
+ Expect(IBaz.BitField.dumpBitfield(IBaz.BitField.VALL), "V0 | V1 | V2 | V3 | VALL");
+ Expect(IBaz.BitField.dumpBitfield((byte)(IBaz.BitField.V1 | IBaz.BitField.V3 | 0xF0)),
+ "V1 | V3 | 0xf0");
+
+ Expect(proxy.toString(), IBaz.kInterfaceName + "@Proxy");
+ }
+
// --- DEATH RECIPIENT TESTING ---
// This must always be done last, since it will kill the native server process
HidlDeathRecipient recipient1 = new HidlDeathRecipient();
diff --git a/test/main.cpp b/test/main.cpp
index 6dda6b2..959eca6 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -582,6 +582,22 @@
// statement can be written here.
}
+TEST_F(HidlTest, EnumToStringTest) {
+ using namespace std::string_literals;
+ using ::android::hardware::tests::foo::V1_0::toString;
+ // toString for enum
+ EXPECT_EQ(toString(IFoo::BitField::V0), "V0"s);
+ EXPECT_EQ(toString(static_cast<IFoo::BitField>(0)), "0"s)
+ << "Invalid enum isn't stringified correctly.";
+ EXPECT_EQ(toString(static_cast<IFoo::BitField>(IFoo::BitField::V0 | IFoo::BitField::V2)), "0x5"s)
+ << "Invalid enum isn't stringified correctly.";
+ // dump bitfields
+ EXPECT_EQ(toString<IFoo::BitField>(0 | IFoo::BitField::V0), "V0 (0x1)"s);
+ EXPECT_EQ(toString<IFoo::BitField>(0 | IFoo::BitField::V0 | IFoo::BitField::V2), "V0 | V2 (0x5)"s);
+ EXPECT_EQ(toString<IFoo::BitField>(0xF), "V0 | V1 | V2 | V3 | VALL (0xf)"s);
+ EXPECT_EQ(toString<IFoo::BitField>(0xFF), "V0 | V1 | V2 | V3 | VALL | 0xf0 (0xff)"s);
+}
+
TEST_F(HidlTest, PingTest) {
EXPECT_OK(manager->ping());
}
diff --git a/utils/Formatter.cpp b/utils/Formatter.cpp
index df3a5a4..4d3771b 100644
--- a/utils/Formatter.cpp
+++ b/utils/Formatter.cpp
@@ -86,6 +86,16 @@
return this->block(block);
}
+Formatter &Formatter::sTry(std::function<void(void)> block) {
+ (*this) << "try ";
+ return this->block(block);
+}
+
+Formatter &Formatter::sCatch(const std::string &exception, std::function<void(void)> block) {
+ (*this) << " catch (" << exception << ") ";
+ return this->block(block);
+}
+
Formatter &Formatter::operator<<(const std::string &out) {
const size_t len = out.length();
size_t start = 0;
diff --git a/utils/include/hidl-util/Formatter.h b/utils/include/hidl-util/Formatter.h
index 7a27287..d1f76fb 100644
--- a/utils/include/hidl-util/Formatter.h
+++ b/utils/include/hidl-util/Formatter.h
@@ -79,6 +79,15 @@
Formatter &sElseIf(const std::string &cond, std::function<void(void)> block);
Formatter &sElse(std::function<void(void)> block);
+ // out.sTry([&] {
+ // out << "throw RemoteException();\n"
+ // }).sCatch("RemoteException ex", [&] {
+ // out << "ex.printStackTrace();\n"
+ // }).endl();
+ // note that there will be a space before the "catch"-s.
+ Formatter &sTry(std::function<void(void)> block);
+ Formatter &sCatch(const std::string &exception, std::function<void(void)> block);
+
Formatter &operator<<(const std::string &out);
Formatter &operator<<(size_t n);