1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "link/Linkers.h"
18 
19 #include "test/Test.h"
20 
21 using ::testing::IsNull;
22 using ::testing::NotNull;
23 
24 namespace aapt {
25 
26 class XmlReferenceLinkerTest : public ::testing::Test {
27  public:
SetUp()28   void SetUp() override {
29     context_ = test::ContextBuilder()
30                    .SetCompilationPackage("com.app.test")
31                    .SetNameManglerPolicy(NameManglerPolicy{"com.app.test", {"com.android.support"}})
32                    .AddSymbolSource(
33                        test::StaticSymbolSourceBuilder()
34                            .AddPublicSymbol("android:attr/layout_width", ResourceId(0x01010000),
35                                             test::AttributeBuilder()
36                                                 .SetTypeMask(android::ResTable_map::TYPE_ENUM |
37                                                              android::ResTable_map::TYPE_DIMENSION)
38                                                 .AddItem("match_parent", 0xffffffff)
39                                                 .Build())
40                            .AddPublicSymbol("android:attr/background", ResourceId(0x01010001),
41                                             test::AttributeBuilder()
42                                                 .SetTypeMask(android::ResTable_map::TYPE_COLOR)
43                                                 .Build())
44                            .AddPublicSymbol("android:attr/attr", ResourceId(0x01010002),
45                                             test::AttributeBuilder().Build())
46                            .AddPublicSymbol("android:attr/text", ResourceId(0x01010003),
47                                             test::AttributeBuilder()
48                                                 .SetTypeMask(android::ResTable_map::TYPE_STRING)
49                                                 .Build())
50 
51                            // Add one real symbol that was introduces in v21
52                            .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
53                                             test::AttributeBuilder().Build())
54 
55                            // Private symbol.
56                            .AddSymbol("android:color/hidden", ResourceId(0x01020001))
57 
58                            .AddPublicSymbol("android:id/id", ResourceId(0x01030000))
59                            .AddSymbol("com.app.test:id/id", ResourceId(0x7f030000))
60                            .AddSymbol("com.app.test:color/green", ResourceId(0x7f020000))
61                            .AddSymbol("com.app.test:color/red", ResourceId(0x7f020001))
62                            .AddSymbol("com.app.test:attr/colorAccent", ResourceId(0x7f010000),
63                                       test::AttributeBuilder()
64                                           .SetTypeMask(android::ResTable_map::TYPE_COLOR)
65                                           .Build())
66                            .AddPublicSymbol("com.app.test:attr/com.android.support$colorAccent",
67                                             ResourceId(0x7f010001),
68                                             test::AttributeBuilder()
69                                                 .SetTypeMask(android::ResTable_map::TYPE_COLOR)
70                                                 .Build())
71                            .AddPublicSymbol("com.app.test:attr/attr", ResourceId(0x7f010002),
72                                             test::AttributeBuilder().Build())
73                            .Build())
74                    .Build();
75   }
76 
77  protected:
78   std::unique_ptr<IAaptContext> context_;
79 };
80 
TEST_F(XmlReferenceLinkerTest,LinkBasicAttributes)81 TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
82   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
83       <View xmlns:android="http://schemas.android.com/apk/res/android"
84             android:layout_width="match_parent"
85             android:background="@color/green"
86             android:text="hello"
87             android:attr="\?hello"
88             nonAaptAttr="1"
89             nonAaptAttrRef="@id/id"
90             class="hello" />)");
91 
92   XmlReferenceLinker linker;
93   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
94 
95   xml::Element* view_el = doc->root.get();
96   ASSERT_THAT(view_el, NotNull());
97 
98   xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
99   ASSERT_THAT(xml_attr, NotNull());
100   ASSERT_TRUE(xml_attr->compiled_attribute);
101   EXPECT_EQ(make_value(ResourceId(0x01010000)), xml_attr->compiled_attribute.value().id);
102   EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
103 
104   xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "background");
105   ASSERT_THAT(xml_attr, NotNull());
106   ASSERT_TRUE(xml_attr->compiled_attribute);
107   EXPECT_EQ(make_value(ResourceId(0x01010001)), xml_attr->compiled_attribute.value().id);
108   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
109   ASSERT_THAT(ref, NotNull());
110   EXPECT_EQ(make_value(test::ParseNameOrDie("color/green")), ref->name);  // Make sure the name
111                                                                           // didn't change.
112   EXPECT_EQ(make_value(ResourceId(0x7f020000)), ref->id);
113 
114   xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
115   ASSERT_THAT(xml_attr, NotNull());
116   EXPECT_TRUE(xml_attr->compiled_attribute);
117   EXPECT_THAT(xml_attr->compiled_value, IsNull());  // Strings don't get compiled for memory sake.
118 
119   xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
120   ASSERT_THAT(xml_attr, NotNull());
121   EXPECT_TRUE(xml_attr->compiled_attribute);
122   EXPECT_THAT(xml_attr->compiled_value, IsNull());  // Should be a plain string.
123 
124   xml_attr = view_el->FindAttribute("", "nonAaptAttr");
125   ASSERT_THAT(xml_attr, NotNull());
126   EXPECT_FALSE(xml_attr->compiled_attribute);
127   EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
128 
129   xml_attr = view_el->FindAttribute("", "nonAaptAttrRef");
130   ASSERT_THAT(xml_attr, NotNull());
131   EXPECT_FALSE(xml_attr->compiled_attribute);
132   EXPECT_THAT(ValueCast<Reference>(xml_attr->compiled_value.get()), NotNull());
133 
134   xml_attr = view_el->FindAttribute("", "class");
135   ASSERT_THAT(xml_attr, NotNull());
136   EXPECT_FALSE(xml_attr->compiled_attribute);
137   EXPECT_THAT(xml_attr->compiled_value, IsNull());
138 }
139 
140 TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
141   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
142       <View xmlns:android="http://schemas.android.com/apk/res/android"
143           android:colorAccent="@android:color/hidden" />)");
144 
145   XmlReferenceLinker linker;
146   ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
147 }
148 
149 TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
150   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
151     <View xmlns:android="http://schemas.android.com/apk/res/android"
152           android:colorAccent="@*android:color/hidden" />)");
153 
154   XmlReferenceLinker linker;
155   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
156 }
157 
158 TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
159   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
160       <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
161           support:colorAccent="#ff0000" />)");
162 
163   XmlReferenceLinker linker;
164   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
165 
166   xml::Element* view_el = doc->root.get();
167   ASSERT_THAT(view_el, NotNull());
168 
169   xml::Attribute* xml_attr =
170       view_el->FindAttribute(xml::BuildPackageNamespace("com.android.support"), "colorAccent");
171   ASSERT_THAT(xml_attr, NotNull());
172   ASSERT_TRUE(xml_attr->compiled_attribute);
173   EXPECT_EQ(make_value(ResourceId(0x7f010001)), xml_attr->compiled_attribute.value().id);
174   EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
175 }
176 
177 TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
178   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
179       <View xmlns:app="http://schemas.android.com/apk/res-auto"
180           app:colorAccent="@app:color/red" />)");
181 
182   XmlReferenceLinker linker;
183   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
184 
185   xml::Element* view_el = doc->root.get();
186   ASSERT_THAT(view_el, NotNull());
187 
188   xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
189   ASSERT_THAT(xml_attr, NotNull());
190   ASSERT_TRUE(xml_attr->compiled_attribute);
191   EXPECT_EQ(make_value(ResourceId(0x7f010000)), xml_attr->compiled_attribute.value().id);
192   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
193   ASSERT_THAT(ref, NotNull());
194   ASSERT_TRUE(ref->name);
195   EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);
196 }
197 
198 TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
199   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
200       <View xmlns:app="http://schemas.android.com/apk/res/android" app:attr="@app:id/id">
201         <View xmlns:app="http://schemas.android.com/apk/res/com.app.test" app:attr="@app:id/id"/>
202       </View>)");
203 
204   XmlReferenceLinker linker;
205   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
206 
207   xml::Element* view_el = doc->root.get();
208   ASSERT_THAT(view_el, NotNull());
209 
210   // All attributes and references in this element should be referring to
211   // "android" (0x01).
212   xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
213   ASSERT_THAT(xml_attr, NotNull());
214   ASSERT_TRUE(xml_attr->compiled_attribute);
215   EXPECT_EQ(make_value(ResourceId(0x01010002)), xml_attr->compiled_attribute.value().id);
216   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
217   ASSERT_THAT(ref, NotNull());
218   EXPECT_EQ(make_value(ResourceId(0x01030000)), ref->id);
219 
220   ASSERT_FALSE(view_el->GetChildElements().empty());
221   view_el = view_el->GetChildElements().front();
222   ASSERT_THAT(view_el, NotNull());
223 
224   // All attributes and references in this element should be referring to
225   // "com.app.test" (0x7f).
226   xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
227   ASSERT_THAT(xml_attr, NotNull());
228   ASSERT_TRUE(xml_attr->compiled_attribute);
229   EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
230   ref = ValueCast<Reference>(xml_attr->compiled_value.get());
231   ASSERT_THAT(ref, NotNull());
232   EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
233 }
234 
235 TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
236   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
237       <View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
238           android:attr="@id/id"/>)");
239 
240   XmlReferenceLinker linker;
241   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
242 
243   xml::Element* view_el = doc->root.get();
244   ASSERT_THAT(view_el, NotNull());
245 
246   // All attributes and references in this element should be referring to
247   // "com.app.test" (0x7f).
248   xml::Attribute* xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
249   ASSERT_THAT(xml_attr, NotNull());
250   ASSERT_TRUE(xml_attr->compiled_attribute);
251   EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
252   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
253   ASSERT_THAT(ref, NotNull());
254   EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
255 }
256 
257 }  // namespace aapt
258