AAPT2: Fix issue where @null was wrongly encoded

@null must be encoded as TYPE_REFERENCE with a value of
0. TYPE_NULL is used by the runtime as a placeholder when resolving
style attributes. If we set a style attribute to TYPE_NULL,
the runtime will throw. The runtime will convert a TYPE_REFERENCE
with value 0 to a proper null value.

Change-Id: Id983ca7e1fbee3124dddafe32f1b5741b824225b
diff --git a/tools/aapt2/BinaryResourceParser.cpp b/tools/aapt2/BinaryResourceParser.cpp
index 620f0fe..3559f43 100644
--- a/tools/aapt2/BinaryResourceParser.cpp
+++ b/tools/aapt2/BinaryResourceParser.cpp
@@ -697,8 +697,7 @@
 
         // This is not an unresolved symbol, so it must be the magic @null reference.
         Res_value nullType = {};
-        nullType.dataType = Res_value::TYPE_NULL;
-        nullType.data = Res_value::DATA_NULL_UNDEFINED;
+        nullType.dataType = Res_value::TYPE_REFERENCE;
         return util::make_unique<BinaryPrimitive>(nullType);
     }
 
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 59915a2..13f916b 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -193,18 +193,18 @@
 
 std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseNullOrEmpty(const StringPiece16& str) {
     StringPiece16 trimmedStr(util::trimWhitespace(str));
-    uint32_t data = 0;
+    android::Res_value value = {};
     if (trimmedStr == u"@null") {
-        data = android::Res_value::DATA_NULL_UNDEFINED;
+        // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
+        // Instead we set the data type to TYPE_REFERENCE with a value of 0.
+        value.dataType = android::Res_value::TYPE_REFERENCE;
     } else if (trimmedStr == u"@empty") {
-        data = android::Res_value::DATA_NULL_EMPTY;
+        // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
+        value.dataType = android::Res_value::TYPE_NULL;
+        value.data = android::Res_value::DATA_NULL_EMPTY;
     } else {
         return {};
     }
-
-    android::Res_value value = {};
-    value.dataType = android::Res_value::TYPE_NULL;
-    value.data = data;
     return util::make_unique<BinaryPrimitive>(value);
 }
 
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 3d8a2f0..a93d0ff 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -191,6 +191,32 @@
     EXPECT_EQ(std::u16string(u"?123"), *str->value);
 }
 
+TEST_F(ResourceParserTest, ParseNull) {
+    std::string input = "<integer name=\"foo\">@null</integer>";
+    ASSERT_TRUE(testParse(input));
+
+    // The Android runtime treats a value of android::Res_value::TYPE_NULL as
+    // a non-existing value, and this causes problems in styles when trying to resolve
+    // an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
+    // with a data value of 0.
+    const BinaryPrimitive* integer = findResource<BinaryPrimitive>(ResourceName{
+            u"android", ResourceType::kInteger, u"foo" });
+    ASSERT_NE(nullptr, integer);
+    EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType);
+    EXPECT_EQ(0u, integer->value.data);
+}
+
+TEST_F(ResourceParserTest, ParseEmpty) {
+    std::string input = "<integer name=\"foo\">@empty</integer>";
+    ASSERT_TRUE(testParse(input));
+
+    const BinaryPrimitive* integer = findResource<BinaryPrimitive>(ResourceName{
+            u"android", ResourceType::kInteger, u"foo" });
+    ASSERT_NE(nullptr, integer);
+    EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
+    EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
+}
+
 TEST_F(ResourceParserTest, ParseAttr) {
     std::string input = "<attr name=\"foo\" format=\"string\"/>\n"
                         "<attr name=\"bar\"/>";