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
21namespace aapt {
22
23class XmlReferenceLinkerTest : public ::testing::Test {
24 public:
25  void SetUp() override {
26    context_ = test::ContextBuilder()
27                   .SetCompilationPackage("com.app.test")
28                   .SetNameManglerPolicy(NameManglerPolicy{"com.app.test", {"com.android.support"}})
29                   .AddSymbolSource(
30                       test::StaticSymbolSourceBuilder()
31                           .AddPublicSymbol("android:attr/layout_width", ResourceId(0x01010000),
32                                            test::AttributeBuilder()
33                                                .SetTypeMask(android::ResTable_map::TYPE_ENUM |
34                                                             android::ResTable_map::TYPE_DIMENSION)
35                                                .AddItem("match_parent", 0xffffffff)
36                                                .Build())
37                           .AddPublicSymbol("android:attr/background", ResourceId(0x01010001),
38                                            test::AttributeBuilder()
39                                                .SetTypeMask(android::ResTable_map::TYPE_COLOR)
40                                                .Build())
41                           .AddPublicSymbol("android:attr/attr", ResourceId(0x01010002),
42                                            test::AttributeBuilder().Build())
43                           .AddPublicSymbol("android:attr/text", ResourceId(0x01010003),
44                                            test::AttributeBuilder()
45                                                .SetTypeMask(android::ResTable_map::TYPE_STRING)
46                                                .Build())
47
48                           // Add one real symbol that was introduces in v21
49                           .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
50                                            test::AttributeBuilder().Build())
51
52                           // Private symbol.
53                           .AddSymbol("android:color/hidden", ResourceId(0x01020001))
54
55                           .AddPublicSymbol("android:id/id", ResourceId(0x01030000))
56                           .AddSymbol("com.app.test:id/id", ResourceId(0x7f030000))
57                           .AddSymbol("com.app.test:color/green", ResourceId(0x7f020000))
58                           .AddSymbol("com.app.test:color/red", ResourceId(0x7f020001))
59                           .AddSymbol("com.app.test:attr/colorAccent", ResourceId(0x7f010000),
60                                      test::AttributeBuilder()
61                                          .SetTypeMask(android::ResTable_map::TYPE_COLOR)
62                                          .Build())
63                           .AddPublicSymbol("com.app.test:attr/com.android.support$colorAccent",
64                                            ResourceId(0x7f010001),
65                                            test::AttributeBuilder()
66                                                .SetTypeMask(android::ResTable_map::TYPE_COLOR)
67                                                .Build())
68                           .AddPublicSymbol("com.app.test:attr/attr", ResourceId(0x7f010002),
69                                            test::AttributeBuilder().Build())
70                           .Build())
71                   .Build();
72  }
73
74 protected:
75  std::unique_ptr<IAaptContext> context_;
76};
77
78TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
79  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
80        <View xmlns:android="http://schemas.android.com/apk/res/android"
81              android:layout_width="match_parent"
82              android:background="@color/green"
83              android:text="hello"
84              android:attr="\?hello"
85              nonAaptAttr="1"
86              nonAaptAttrRef="@id/id"
87              class="hello" />)EOF");
88
89  XmlReferenceLinker linker;
90  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
91
92  xml::Element* view_el = xml::FindRootElement(doc.get());
93  ASSERT_NE(nullptr, view_el);
94
95  xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
96  ASSERT_NE(nullptr, xml_attr);
97  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
98  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
99  EXPECT_EQ(ResourceId(0x01010000), xml_attr->compiled_attribute.value().id.value());
100  ASSERT_NE(nullptr, xml_attr->compiled_value);
101  ASSERT_NE(nullptr, ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()));
102
103  xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "background");
104  ASSERT_NE(nullptr, xml_attr);
105  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
106  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
107  EXPECT_EQ(ResourceId(0x01010001), xml_attr->compiled_attribute.value().id.value());
108  ASSERT_NE(nullptr, xml_attr->compiled_value);
109  Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
110  ASSERT_NE(nullptr, ref);
111  AAPT_ASSERT_TRUE(ref->name);
112  EXPECT_EQ(test::ParseNameOrDie("color/green"), ref->name.value());  // Make sure the name
113                                                                      // didn't change.
114  AAPT_ASSERT_TRUE(ref->id);
115  EXPECT_EQ(ResourceId(0x7f020000), ref->id.value());
116
117  xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
118  ASSERT_NE(nullptr, xml_attr);
119  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
120  ASSERT_FALSE(xml_attr->compiled_value);  // Strings don't get compiled for memory sake.
121
122  xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
123  ASSERT_NE(nullptr, xml_attr);
124  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
125  ASSERT_FALSE(xml_attr->compiled_value);  // Should be a plain string.
126
127  xml_attr = view_el->FindAttribute("", "nonAaptAttr");
128  ASSERT_NE(nullptr, xml_attr);
129  AAPT_ASSERT_FALSE(xml_attr->compiled_attribute);
130  ASSERT_NE(nullptr, xml_attr->compiled_value);
131  ASSERT_NE(nullptr, ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()));
132
133  xml_attr = view_el->FindAttribute("", "nonAaptAttrRef");
134  ASSERT_NE(nullptr, xml_attr);
135  AAPT_ASSERT_FALSE(xml_attr->compiled_attribute);
136  ASSERT_NE(nullptr, xml_attr->compiled_value);
137  ASSERT_NE(nullptr, ValueCast<Reference>(xml_attr->compiled_value.get()));
138
139  xml_attr = view_el->FindAttribute("", "class");
140  ASSERT_NE(nullptr, xml_attr);
141  AAPT_ASSERT_FALSE(xml_attr->compiled_attribute);
142  ASSERT_EQ(nullptr, xml_attr->compiled_value);
143}
144
145TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
146  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
147        <View xmlns:android="http://schemas.android.com/apk/res/android"
148              android:colorAccent="@android:color/hidden" />)EOF");
149
150  XmlReferenceLinker linker;
151  ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
152}
153
154TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
155  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
156    <View xmlns:android="http://schemas.android.com/apk/res/android"
157          android:colorAccent="@*android:color/hidden" />)EOF");
158
159  XmlReferenceLinker linker;
160  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
161}
162
163TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
164  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
165            <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
166                  support:colorAccent="#ff0000" />)EOF");
167
168  XmlReferenceLinker linker;
169  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
170
171  xml::Element* view_el = xml::FindRootElement(doc.get());
172  ASSERT_NE(view_el, nullptr);
173
174  xml::Attribute* xml_attr =
175      view_el->FindAttribute(xml::BuildPackageNamespace("com.android.support"), "colorAccent");
176  ASSERT_NE(xml_attr, nullptr);
177  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
178  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
179  EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x7f010001));
180  ASSERT_NE(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), nullptr);
181}
182
183TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
184  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
185            <View xmlns:app="http://schemas.android.com/apk/res-auto"
186                  app:colorAccent="@app:color/red" />)EOF");
187
188  XmlReferenceLinker linker;
189  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
190
191  xml::Element* view_el = xml::FindRootElement(doc.get());
192  ASSERT_NE(view_el, nullptr);
193
194  xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
195  ASSERT_NE(xml_attr, nullptr);
196  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
197  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
198  EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x7f010000));
199  Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
200  ASSERT_NE(ref, nullptr);
201  AAPT_ASSERT_TRUE(ref->name);
202  AAPT_ASSERT_TRUE(ref->id);
203  EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
204}
205
206TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
207  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
208            <View xmlns:app="http://schemas.android.com/apk/res/android"
209                  app:attr="@app:id/id">
210              <View xmlns:app="http://schemas.android.com/apk/res/com.app.test"
211                    app:attr="@app:id/id"/>
212            </View>)EOF");
213
214  XmlReferenceLinker linker;
215  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
216
217  xml::Element* view_el = xml::FindRootElement(doc.get());
218  ASSERT_NE(view_el, nullptr);
219
220  // All attributes and references in this element should be referring to
221  // "android" (0x01).
222  xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
223  ASSERT_NE(xml_attr, nullptr);
224  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
225  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
226  EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x01010002));
227  Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
228  ASSERT_NE(ref, nullptr);
229  AAPT_ASSERT_TRUE(ref->id);
230  EXPECT_EQ(ref->id.value(), ResourceId(0x01030000));
231
232  ASSERT_FALSE(view_el->GetChildElements().empty());
233  view_el = view_el->GetChildElements().front();
234  ASSERT_NE(view_el, nullptr);
235
236  // All attributes and references in this element should be referring to
237  // "com.app.test" (0x7f).
238  xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
239  ASSERT_NE(xml_attr, nullptr);
240  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
241  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
242  EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x7f010002));
243  ref = ValueCast<Reference>(xml_attr->compiled_value.get());
244  ASSERT_NE(ref, nullptr);
245  AAPT_ASSERT_TRUE(ref->id);
246  EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
247}
248
249TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
250  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
251            <View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
252                  android:attr="@id/id"/>)EOF");
253
254  XmlReferenceLinker linker;
255  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
256
257  xml::Element* view_el = xml::FindRootElement(doc.get());
258  ASSERT_NE(view_el, nullptr);
259
260  // All attributes and references in this element should be referring to
261  // "com.app.test" (0x7f).
262  xml::Attribute* xml_attr =
263      view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
264  ASSERT_NE(xml_attr, nullptr);
265  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
266  AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
267  EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x7f010002));
268  Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
269  ASSERT_NE(ref, nullptr);
270  AAPT_ASSERT_TRUE(ref->id);
271  EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
272}
273
274}  // namespace aapt
275