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                            .AddPublicSymbol("android:attr/angle", ResourceId(0x01010004),
51                                             test::AttributeBuilder().Build())
52 
53                            // Add one real symbol that was introduces in v21
54                            .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
55                                             test::AttributeBuilder().Build())
56 
57                            // Private symbol.
58                            .AddSymbol("android:color/hidden", ResourceId(0x01020001))
59 
60                            .AddPublicSymbol("android:id/id", ResourceId(0x01030000))
61                            .AddSymbol("com.app.test:id/id", ResourceId(0x7f030000))
62                            .AddSymbol("com.app.test:color/green", ResourceId(0x7f020000))
63                            .AddSymbol("com.app.test:color/red", ResourceId(0x7f020001))
64                            .AddSymbol("com.app.test:attr/colorAccent", ResourceId(0x7f010000),
65                                       test::AttributeBuilder()
66                                           .SetTypeMask(android::ResTable_map::TYPE_COLOR)
67                                           .Build())
68                            .AddPublicSymbol("com.app.test:attr/com.android.support$colorAccent",
69                                             ResourceId(0x7f010001),
70                                             test::AttributeBuilder()
71                                                 .SetTypeMask(android::ResTable_map::TYPE_COLOR)
72                                                 .Build())
73                            .AddPublicSymbol("com.app.test:attr/attr", ResourceId(0x7f010002),
74                                             test::AttributeBuilder().Build())
75                            .Build())
76                    .Build();
77   }
78 
79  protected:
80   std::unique_ptr<test::Context> context_;
81 };
82 
TEST_F(XmlReferenceLinkerTest,LinkBasicAttributes)83 TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
84   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
85       <View xmlns:android="http://schemas.android.com/apk/res/android"
86             android:layout_width="match_parent"
87             android:background="@color/green"
88             android:text="hello"
89             android:attr="\?hello"
90             nonAaptAttr="1"
91             nonAaptAttrRef="@id/id"
92             class="hello" />)");
93 
94   XmlReferenceLinker linker;
95   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
96 
97   xml::Element* view_el = doc->root.get();
98   ASSERT_THAT(view_el, NotNull());
99 
100   xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
101   ASSERT_THAT(xml_attr, NotNull());
102   ASSERT_TRUE(xml_attr->compiled_attribute);
103   EXPECT_EQ(make_value(ResourceId(0x01010000)), xml_attr->compiled_attribute.value().id);
104   EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
105 
106   xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "background");
107   ASSERT_THAT(xml_attr, NotNull());
108   ASSERT_TRUE(xml_attr->compiled_attribute);
109   EXPECT_EQ(make_value(ResourceId(0x01010001)), xml_attr->compiled_attribute.value().id);
110   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
111   ASSERT_THAT(ref, NotNull());
112   EXPECT_EQ(make_value(test::ParseNameOrDie("color/green")), ref->name);  // Make sure the name
113                                                                           // didn't change.
114   EXPECT_EQ(make_value(ResourceId(0x7f020000)), ref->id);
115 
116   xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
117   ASSERT_THAT(xml_attr, NotNull());
118   EXPECT_TRUE(xml_attr->compiled_attribute);
119   EXPECT_THAT(xml_attr->compiled_value, IsNull());  // Strings don't get compiled for memory sake.
120 
121   xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
122   ASSERT_THAT(xml_attr, NotNull());
123   EXPECT_TRUE(xml_attr->compiled_attribute);
124   EXPECT_THAT(xml_attr->compiled_value, IsNull());  // Should be a plain string.
125 
126   xml_attr = view_el->FindAttribute("", "nonAaptAttr");
127   ASSERT_THAT(xml_attr, NotNull());
128   EXPECT_FALSE(xml_attr->compiled_attribute);
129   EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
130 
131   xml_attr = view_el->FindAttribute("", "nonAaptAttrRef");
132   ASSERT_THAT(xml_attr, NotNull());
133   EXPECT_FALSE(xml_attr->compiled_attribute);
134   EXPECT_THAT(ValueCast<Reference>(xml_attr->compiled_value.get()), NotNull());
135 
136   xml_attr = view_el->FindAttribute("", "class");
137   ASSERT_THAT(xml_attr, NotNull());
138   EXPECT_FALSE(xml_attr->compiled_attribute);
139   EXPECT_THAT(xml_attr->compiled_value, IsNull());
140 }
141 
142 TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
143   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
144       <View xmlns:android="http://schemas.android.com/apk/res/android"
145           android:colorAccent="@android:color/hidden" />)");
146 
147   XmlReferenceLinker linker;
148   ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
149 }
150 
151 TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
152   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
153     <View xmlns:android="http://schemas.android.com/apk/res/android"
154           android:colorAccent="@*android:color/hidden" />)");
155 
156   XmlReferenceLinker linker;
157   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
158 }
159 
160 TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
161   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
162       <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
163           support:colorAccent="#ff0000" />)");
164 
165   XmlReferenceLinker linker;
166   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
167 
168   xml::Element* view_el = doc->root.get();
169   ASSERT_THAT(view_el, NotNull());
170 
171   xml::Attribute* xml_attr =
172       view_el->FindAttribute(xml::BuildPackageNamespace("com.android.support"), "colorAccent");
173   ASSERT_THAT(xml_attr, NotNull());
174   ASSERT_TRUE(xml_attr->compiled_attribute);
175   EXPECT_EQ(make_value(ResourceId(0x7f010001)), xml_attr->compiled_attribute.value().id);
176   EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
177 }
178 
179 TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
180   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
181       <View xmlns:app="http://schemas.android.com/apk/res-auto"
182           app:colorAccent="@app:color/red" />)");
183 
184   XmlReferenceLinker linker;
185   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
186 
187   xml::Element* view_el = doc->root.get();
188   ASSERT_THAT(view_el, NotNull());
189 
190   xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
191   ASSERT_THAT(xml_attr, NotNull());
192   ASSERT_TRUE(xml_attr->compiled_attribute);
193   EXPECT_EQ(make_value(ResourceId(0x7f010000)), xml_attr->compiled_attribute.value().id);
194   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
195   ASSERT_THAT(ref, NotNull());
196   ASSERT_TRUE(ref->name);
197   EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);
198 }
199 
200 TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
201   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
202       <View xmlns:app="http://schemas.android.com/apk/res/android" app:attr="@app:id/id">
203         <View xmlns:app="http://schemas.android.com/apk/res/com.app.test" app:attr="@app:id/id"/>
204       </View>)");
205 
206   XmlReferenceLinker linker;
207   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
208 
209   xml::Element* view_el = doc->root.get();
210   ASSERT_THAT(view_el, NotNull());
211 
212   // All attributes and references in this element should be referring to
213   // "android" (0x01).
214   xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
215   ASSERT_THAT(xml_attr, NotNull());
216   ASSERT_TRUE(xml_attr->compiled_attribute);
217   EXPECT_EQ(make_value(ResourceId(0x01010002)), xml_attr->compiled_attribute.value().id);
218   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
219   ASSERT_THAT(ref, NotNull());
220   EXPECT_EQ(make_value(ResourceId(0x01030000)), ref->id);
221 
222   ASSERT_FALSE(view_el->GetChildElements().empty());
223   view_el = view_el->GetChildElements().front();
224   ASSERT_THAT(view_el, NotNull());
225 
226   // All attributes and references in this element should be referring to
227   // "com.app.test" (0x7f).
228   xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
229   ASSERT_THAT(xml_attr, NotNull());
230   ASSERT_TRUE(xml_attr->compiled_attribute);
231   EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
232   ref = ValueCast<Reference>(xml_attr->compiled_value.get());
233   ASSERT_THAT(ref, NotNull());
234   EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
235 }
236 
237 TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
238   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
239       <View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
240           android:attr="@id/id"/>)");
241 
242   XmlReferenceLinker linker;
243   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
244 
245   xml::Element* view_el = doc->root.get();
246   ASSERT_THAT(view_el, NotNull());
247 
248   // All attributes and references in this element should be referring to
249   // "com.app.test" (0x7f).
250   xml::Attribute* xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
251   ASSERT_THAT(xml_attr, NotNull());
252   ASSERT_TRUE(xml_attr->compiled_attribute);
253   EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
254   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
255   ASSERT_THAT(ref, NotNull());
256   EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
257 }
258 
259 
260 TEST_F(XmlReferenceLinkerTest, AddAngleOnGradientForAndroidQ) {
261   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
262     <gradient />)");
263 
264   XmlReferenceLinker linker;
265   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
266 
267   xml::Element* gradient_el = doc->root.get();
268   ASSERT_THAT(gradient_el, NotNull());
269 
270   xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
271   ASSERT_THAT(xml_attr, NotNull());
272   ASSERT_TRUE(xml_attr->compiled_attribute);
273   EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
274 
275   BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
276   ASSERT_THAT(value, NotNull());
277   EXPECT_EQ(value->value.dataType, android::Res_value::TYPE_INT_DEC);
278   EXPECT_EQ(value->value.data, 0U);
279 }
280 
281 TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForAndroidQ) {
282   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
283   <gradient xmlns:android="http://schemas.android.com/apk/res/android"
284       android:angle="90"/>)");
285 
286   XmlReferenceLinker linker;
287   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
288 
289   xml::Element* gradient_el = doc->root.get();
290   ASSERT_THAT(gradient_el, NotNull());
291 
292   xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
293   ASSERT_THAT(xml_attr, NotNull());
294   ASSERT_TRUE(xml_attr->compiled_attribute);
295   EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
296 
297   BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
298   ASSERT_THAT(value, NotNull());
299   EXPECT_EQ(value->value.dataType, android::Res_value::TYPE_INT_DEC);
300   EXPECT_EQ(value->value.data, 90U);
301 }
302 
303 TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForPostAndroidQ) {
304   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
305   <gradient xmlns:android="http://schemas.android.com/apk/res/android" />)");
306   context_->SetMinSdkVersion(30);
307 
308   XmlReferenceLinker linker;
309   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
310 
311   xml::Element* gradient_el = doc->root.get();
312   ASSERT_THAT(gradient_el, NotNull());
313 
314   xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
315   ASSERT_THAT(xml_attr, IsNull());
316 }
317 
318 }  // namespace aapt
319