AAPT2: Fixup namespace implementation

A few pieces were missing in the namespace mangling implementation.
Namespace aware libraries now work, along with R class generation.

Bug: 64706588
Test: make AaptTestNamespace_App
Change-Id: I12f78d6aa909e782c0faf7ceaa36058f2e6c864a
This commit is contained in:
Adam Lesinski
2017-08-15 21:32:49 -07:00
parent 668feb252a
commit 1ef0fa9d72
76 changed files with 822 additions and 506 deletions

View File

@@ -69,8 +69,7 @@ class NameMangler {
* The mangled name should contain symbols that are illegal to define in XML,
* so that there will never be name mangling collisions.
*/
static std::string MangleEntry(const std::string& package,
const std::string& name) {
static std::string MangleEntry(const std::string& package, const std::string& name) {
return package + "$" + name;
}
@@ -86,8 +85,8 @@ class NameMangler {
}
out_package->assign(out_name->data(), pivot);
out_name->assign(out_name->data() + pivot + 1,
out_name->size() - (pivot + 1));
std::string new_name = out_name->substr(pivot + 1);
*out_name = std::move(new_name);
return true;
}

View File

@@ -605,7 +605,7 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
if (processed_item) {
// Fix up the reference.
if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
TransformReferenceFromNamespace(parser, "", ref);
ResolvePackage(parser, ref);
}
return processed_item;
}
@@ -1074,15 +1074,13 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
return false;
}
Maybe<Reference> maybe_key =
ResourceUtils::ParseXmlAttributeName(maybe_name.value());
Maybe<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
if (!maybe_key) {
diag_->Error(DiagMessage(source) << "invalid attribute name '"
<< maybe_name.value() << "'");
diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'");
return false;
}
TransformReferenceFromNamespace(parser, "", &maybe_key.value());
ResolvePackage(parser, &maybe_key.value());
maybe_key.value().SetSource(source);
std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
@@ -1091,8 +1089,7 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
return false;
}
style->entries.push_back(
Style::Entry{std::move(maybe_key.value()), std::move(value)});
style->entries.push_back(Style::Entry{std::move(maybe_key.value()), std::move(value)});
return true;
}
@@ -1104,21 +1101,18 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
if (maybe_parent) {
// If the parent is empty, we don't have a parent, but we also don't infer
// either.
// If the parent is empty, we don't have a parent, but we also don't infer either.
if (!maybe_parent.value().empty()) {
std::string err_str;
style->parent = ResourceUtils::ParseStyleParentReference(
maybe_parent.value(), &err_str);
style->parent = ResourceUtils::ParseStyleParentReference(maybe_parent.value(), &err_str);
if (!style->parent) {
diag_->Error(DiagMessage(out_resource->source) << err_str);
return false;
}
// Transform the namespace prefix to the actual package name, and mark the
// reference as
// Transform the namespace prefix to the actual package name, and mark the reference as
// private if appropriate.
TransformReferenceFromNamespace(parser, "", &style->parent.value());
ResolvePackage(parser, &style->parent.value());
}
} else {
@@ -1127,8 +1121,7 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
size_t pos = style_name.find_last_of(u'.');
if (pos != std::string::npos) {
style->parent_inferred = true;
style->parent = Reference(
ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
style->parent = Reference(ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
}
}
@@ -1373,7 +1366,7 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
}
Reference& child_ref = maybe_ref.value();
xml::TransformReferenceFromNamespace(parser, "", &child_ref);
xml::ResolvePackage(parser, &child_ref);
// Create the ParsedResource that will add the attribute to the table.
ParsedResource child_resource;

View File

@@ -457,7 +457,8 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
const Source& src = doc->file.source;
if (context_->IsVerbose()) {
context_->GetDiagnostics()->Note(DiagMessage() << "linking " << src.path);
context_->GetDiagnostics()->Note(DiagMessage()
<< "linking " << src.path << " (" << doc->file.name << ")");
}
XmlReferenceLinker xml_linker;
@@ -505,6 +506,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
for (auto& pkg : table->packages) {
CHECK(!pkg->name.empty()) << "Packages must have names when being linked";
for (auto& type : pkg->types) {
// Sort by config and name, so that we get better locality in the zip file.
config_sorted_files.clear();
@@ -701,7 +704,7 @@ class LinkCommand {
util::make_unique<AssetManagerSymbolSource>();
for (const std::string& path : options_.include_paths) {
if (context_->IsVerbose()) {
context_->GetDiagnostics()->Note(DiagMessage(path) << "loading include path");
context_->GetDiagnostics()->Note(DiagMessage() << "including " << path);
}
// First try to load the file as a static lib.
@@ -819,11 +822,9 @@ class LinkCommand {
return app_info;
}
/**
* Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
* Postcondition: ResourceTable has only one package left. All others are
* stripped, or there is an error and false is returned.
*/
// Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
// Postcondition: ResourceTable has only one package left. All others are
// stripped, or there is an error and false is returned.
bool VerifyNoExternalPackages() {
auto is_ext_package_func = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
return context_->GetCompilationPackage() != pkg->name || !pkg->id ||
@@ -965,10 +966,94 @@ class LinkCommand {
context_->GetDiagnostics()->Error(DiagMessage()
<< "failed writing to '" << out_path
<< "': " << android::base::SystemErrorCodeToString(errno));
return false;
}
return true;
}
bool GenerateJavaClasses() {
// The set of packages whose R class to call in the main classes onResourcesLoaded callback.
std::vector<std::string> packages_to_callback;
JavaClassGeneratorOptions template_options;
template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
template_options.javadoc_annotations = options_.javadoc_annotations;
if (context_->GetPackageType() == PackageType::kStaticLib || options_.generate_non_final_ids) {
template_options.use_final = false;
}
if (context_->GetPackageType() == PackageType::kSharedLib) {
template_options.use_final = false;
template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
}
const StringPiece actual_package = context_->GetCompilationPackage();
StringPiece output_package = context_->GetCompilationPackage();
if (options_.custom_java_package) {
// Override the output java package to the custom one.
output_package = options_.custom_java_package.value();
}
// Generate the private symbols if required.
if (options_.private_symbols) {
packages_to_callback.push_back(options_.private_symbols.value());
// If we defined a private symbols package, we only emit Public symbols
// to the original package, and private and public symbols to the private package.
JavaClassGeneratorOptions options = template_options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
options)) {
return false;
}
}
// Generate copies of the original package R class but with different package names.
// This is to support non-namespaced builds.
for (const std::string& extra_package : options_.extra_java_packages) {
packages_to_callback.push_back(extra_package);
JavaClassGeneratorOptions options = template_options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
return false;
}
}
// Generate R classes for each package that was merged (static library).
// Use the actual package's resources only.
for (const std::string& package : table_merger_->merged_packages()) {
packages_to_callback.push_back(package);
JavaClassGeneratorOptions options = template_options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
if (!WriteJavaFile(&final_table_, package, package, options)) {
return false;
}
}
// Generate the main public R class.
JavaClassGeneratorOptions options = template_options;
// Only generate public symbols if we have a private package.
if (options_.private_symbols) {
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
}
if (options.rewrite_callback_options) {
options.rewrite_callback_options.value().packages_to_callback =
std::move(packages_to_callback);
}
if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
options_.generate_text_symbols_path)) {
return false;
}
return true;
}
bool WriteManifestJavaFile(xml::XmlResource* manifest_xml) {
if (!options_.generate_java_class_path) {
return true;
@@ -1097,15 +1182,17 @@ class LinkCommand {
bool result;
if (options_.no_static_lib_packages) {
// Merge all resources as if they were in the compilation package. This is
// the old behavior of aapt.
// Merge all resources as if they were in the compilation package. This is the old behavior
// of aapt.
// Add the package to the set of --extra-packages so we emit an R.java for
// each library package.
// Add the package to the set of --extra-packages so we emit an R.java for each library
// package.
if (!pkg->name.empty()) {
options_.extra_java_packages.insert(pkg->name);
}
// Clear the package name, so as to make the resources look like they are coming from the
// local package.
pkg->name = "";
if (override) {
result = table_merger_->MergeOverlay(Source(input), table.get(), collection.get());
@@ -1673,8 +1760,7 @@ class LinkCommand {
bool error = false;
{
// AndroidManifest.xml has no resource name, but the CallSite is built
// from the name
// AndroidManifest.xml has no resource name, but the CallSite is built from the name
// (aka, which package the AndroidManifest.xml is coming from).
// So we give it a package name so it can see local resources.
manifest_xml->file.name.package = context_->GetCompilationPackage();
@@ -1727,72 +1813,7 @@ class LinkCommand {
}
if (options_.generate_java_class_path) {
// The set of packages whose R class to call in the main classes
// onResourcesLoaded callback.
std::vector<std::string> packages_to_callback;
JavaClassGeneratorOptions template_options;
template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
template_options.javadoc_annotations = options_.javadoc_annotations;
if (context_->GetPackageType() == PackageType::kStaticLib ||
options_.generate_non_final_ids) {
template_options.use_final = false;
}
if (context_->GetPackageType() == PackageType::kSharedLib) {
template_options.use_final = false;
template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
}
const StringPiece actual_package = context_->GetCompilationPackage();
StringPiece output_package = context_->GetCompilationPackage();
if (options_.custom_java_package) {
// Override the output java package to the custom one.
output_package = options_.custom_java_package.value();
}
// Generate the private symbols if required.
if (options_.private_symbols) {
packages_to_callback.push_back(options_.private_symbols.value());
// If we defined a private symbols package, we only emit Public symbols
// to the original package, and private and public symbols to the
// private package.
JavaClassGeneratorOptions options = template_options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
options)) {
return 1;
}
}
// Generate all the symbols for all extra packages.
for (const std::string& extra_package : options_.extra_java_packages) {
packages_to_callback.push_back(extra_package);
JavaClassGeneratorOptions options = template_options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
return 1;
}
}
// Generate the main public R class.
JavaClassGeneratorOptions options = template_options;
// Only generate public symbols if we have a private package.
if (options_.private_symbols) {
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
}
if (options.rewrite_callback_options) {
options.rewrite_callback_options.value().packages_to_callback =
std::move(packages_to_callback);
}
if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
options_.generate_text_symbols_path)) {
if (!GenerateJavaClasses()) {
return 1;
}
}

View File

@@ -69,10 +69,10 @@ class Visitor : public xml::PackageAwareVisitor {
// Use an empty string for the compilation package because we don't want to default to
// the local package if the user specified name="style" or something. This should just
// be the default namespace.
Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package, {});
Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
if (!maybe_pkg) {
context_->GetDiagnostics()->Error(DiagMessage(src) << "invalid namespace prefix '"
<< name.package << "'");
context_->GetDiagnostics()->Error(DiagMessage(src)
<< "invalid namespace prefix '" << name.package << "'");
error_ = true;
return;
}

View File

@@ -0,0 +1,2 @@
LOCAL_PATH := $(call my-dir)
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -0,0 +1,29 @@
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_PACKAGE_NAME := AaptTestNamespace_App
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_STATIC_ANDROID_LIBRARIES := \
AaptTestNamespace_LibOne \
AaptTestNamespace_LibTwo
LOCAL_AAPT_FLAGS := -v
include $(BUILD_PACKAGE)

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.aapt.namespace.app">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
<application android:theme="@style/AppTheme" android:label="@string/app_name">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:libone="http://schemas.android.com/apk/res/com.android.aapt.namespace.libone"
xmlns:libtwo="http://schemas.android.com/apk/res/com.android.aapt.namespace.libtwo"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.android.aapt.namespace.libtwo.TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="@bool/always_true"
android:text="@libone:string/textview_text"
libtwo:textview_attr="?libone:theme_attr" />
</RelativeLayout>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="app_name">Namespace App</string>
<bool name="always_true">true</bool>
<style name="AppTheme" parent="com.android.aapt.namespace.libone:style/Theme">
<item name="android:colorPrimary">#3F51B5</item>
<item name="android:colorPrimaryDark">#303F9F</item>
<item name="android:colorAccent">#FF4081</item>
<item name="com.android.aapt.namespace.libone:theme_attr">
@com.android.aapt.namespace.libtwo:string/public_string
</item>
</style>
</resources>

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.aapt.namespace.app;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
com.android.aapt.namespace.libtwo.TextView tv = findViewById(R.id.textview);
Toast.makeText(this, tv.getTextViewAttr(), Toast.LENGTH_LONG).show();
}
}

View File

@@ -0,0 +1,28 @@
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_MODULE := AaptTestNamespace_LibOne
LOCAL_MODULE_TAGS := tests
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
# We need this to retain the R.java generated for this library.
LOCAL_JAR_EXCLUDE_FILES := none
include $(BUILD_STATIC_JAVA_LIBRARY)

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.aapt.namespace.libone">
<uses-sdk android:minSdkVersion="21" />
</manifest>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<public type="string" name="textview_text" />
<string name="textview_text">LibOne\'s textview_text string!</string>
<public type="attr" name="theme_attr" />
<attr name="theme_attr" format="string" />
<public type="style" name="Theme" />
<style name="Theme" parent="android:Theme.Material.Light.DarkActionBar">
<item name="theme_attr">[Please override with your own value]</item>
</style>
</resources>

View File

@@ -0,0 +1,29 @@
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_MODULE := AaptTestNamespace_LibTwo
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
# We need this to retain the R.java generated for this library.
LOCAL_JAR_EXCLUDE_FILES := none
include $(BUILD_STATIC_JAVA_LIBRARY)

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.aapt.namespace.libtwo">
<uses-sdk android:minSdkVersion="21" />
</manifest>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<public type="string" name="public_string" />
<string name="public_string">LibTwo\'s public string!</string>
<public type="attr" name="textview_attr" />
<attr name="textview_attr" format="string" />
<declare-styleable name="TextView">
<attr name="textview_attr" />
</declare-styleable>
</resources>

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.aapt.namespace.libtwo;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
public class TextView extends android.widget.TextView {
private String mTextViewAttr;
public TextView(Context context) {
this(context, null);
}
public TextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public TextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TextView,
0, 0);
try {
mTextViewAttr = ta.getString(R.styleable.TextView_textview_attr);
} finally {
ta.recycle();
}
}
public String getTextViewAttr() {
return mTextViewAttr;
}
}

View File

@@ -0,0 +1,2 @@
LOCAL_PATH := $(call my-dir)
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -18,12 +18,12 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_PACKAGE_NAME := AaptTestAppOne
LOCAL_PACKAGE_NAME := AaptTestStaticLib_App
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets $(LOCAL_PATH)/assets2
LOCAL_STATIC_ANDROID_LIBRARIES := \
AaptTestStaticLibOne \
AaptTestStaticLibTwo
AaptTestStaticLib_LibOne \
AaptTestStaticLib_LibTwo
LOCAL_AAPT_FLAGS := --no-version-vectors --no-version-transitions
include $(BUILD_PACKAGE)

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 124 B

After

Width:  |  Height:  |  Size: 124 B

View File

@@ -15,7 +15,6 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content">

View File

@@ -15,7 +15,6 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
<resources>
<style name="App">
<item name="android:background">@color/primary</item>
<item name="android:colorPrimary">@color/primary</item>

View File

@@ -18,11 +18,11 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := AaptTestStaticLibOne
LOCAL_MODULE := AaptTestStaticLib_LibOne
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
# We need this to compile the Java sources of AaptTestStaticLibTwo using javac.
# We need this to compile the Java sources of AaptTestStaticLib_LibTwo using javac.
LOCAL_JAR_EXCLUDE_FILES := none
include $(BUILD_STATIC_JAVA_LIBRARY)

View File

@@ -18,10 +18,10 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := AaptTestStaticLibTwo
LOCAL_MODULE := AaptTestStaticLib_LibTwo
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLibOne
LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLib_LibOne
include $(BUILD_STATIC_JAVA_LIBRARY)

View File

@@ -480,7 +480,7 @@ Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& packa
if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
// The entry name was mangled, and we successfully unmangled it.
// Check that we want to emit this symbol.
if (package_name != unmangled_package) {
if (package_name_to_generate != unmangled_package) {
// Skip the entry if it doesn't belong to the package we're writing.
return {};
}
@@ -579,8 +579,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
continue;
}
// Stay consistent with AAPT and generate an empty type class if the R class
// is public.
// Stay consistent with AAPT and generate an empty type class if the R class is public.
const bool force_creation_if_empty =
(options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);

View File

@@ -21,6 +21,7 @@
#include <unordered_set>
#include "android-base/macros.h"
#include "androidfw/StringPiece.h"
#include "Resource.h"
#include "SdkConstants.h"
@@ -33,18 +34,15 @@ class ResourceTable;
class ResourceEntry;
struct ConfigDescription;
/**
* Defines the location in which a value exists. This determines visibility of
* other package's private symbols.
*/
// Defines the context in which a resource value is defined. Most resources are defined with the
// implicit package name of their compilation context. Understanding the package name of a resource
// allows to determine visibility of other symbols which may or may not have their packages defined.
struct CallSite {
ResourceNameRef resource;
std::string package;
};
/**
* Determines whether a versioned resource should be created. If a versioned
* resource already exists, it takes precedence.
*/
// Determines whether a versioned resource should be created. If a versioned resource already
// exists, it takes precedence.
bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
const ApiVersion sdk_version_to_generate);
@@ -62,39 +60,26 @@ class AutoVersioner : public IResourceTableConsumer {
DISALLOW_COPY_AND_ASSIGN(AutoVersioner);
};
/**
* If any attribute resource values are defined as public, this consumer will
* move all private
* attribute resource values to a private ^private-attr type, avoiding backwards
* compatibility
* issues with new apps running on old platforms.
*
* The Android platform ignores resource attributes it doesn't recognize, so an
* app developer can
* use new attributes in their layout XML files without worrying about
* versioning. This assumption
* actually breaks on older platforms. OEMs may add private attributes that are
* used internally.
* AAPT originally assigned all private attributes IDs immediately proceeding
* the public attributes'
* IDs.
*
* This means that on a newer Android platform, an ID previously assigned to a
* private attribute
* may end up assigned to a public attribute.
*
* App developers assume using the newer attribute is safe on older platforms
* because it will
* be ignored. Instead, the platform thinks the new attribute is an older,
* private attribute and
* will interpret it as such. This leads to unintended styling and exceptions
* thrown due to
* unexpected types.
*
* By moving the private attributes to a completely different type, this ID
* conflict will never
* occur.
*/
// If any attribute resource values are defined as public, this consumer will move all private
// attribute resource values to a private ^private-attr type, avoiding backwards compatibility
// issues with new apps running on old platforms.
//
// The Android platform ignores resource attributes it doesn't recognize, so an app developer can
// use new attributes in their layout XML files without worrying about versioning. This assumption
// actually breaks on older platforms. OEMs may add private attributes that are used internally.
// AAPT originally assigned all private attributes IDs immediately proceeding the public attributes'
// IDs.
//
// This means that on a newer Android platform, an ID previously assigned to a private attribute
// may end up assigned to a public attribute.
//
// App developers assume using the newer attribute is safe on older platforms because it will
// be ignored. Instead, the platform thinks the new attribute is an older, private attribute and
// will interpret it as such. This leads to unintended styling and exceptions thrown due to
// unexpected types.
//
// By moving the private attributes to a completely different type, this ID conflict will never
// occur.
class PrivateAttributeMover : public IResourceTableConsumer {
public:
PrivateAttributeMover() = default;
@@ -126,14 +111,10 @@ class ProductFilter : public IResourceTableConsumer {
std::unordered_set<std::string> products_;
};
/**
* Removes namespace nodes and URI information from the XmlResource.
*
* Once an XmlResource is processed by this consumer, it is no longer able to
* have its attributes
* parsed. As such, this XmlResource must have already been processed by
* XmlReferenceLinker.
*/
// Removes namespace nodes and URI information from the XmlResource.
//
// Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
// parsed. As such, this XmlResource must have already been processed by XmlReferenceLinker.
class XmlNamespaceRemover : public IXmlResourceConsumer {
public:
explicit XmlNamespaceRemover(bool keep_uris = false) : keep_uris_(keep_uris){};
@@ -146,11 +127,8 @@ class XmlNamespaceRemover : public IXmlResourceConsumer {
bool keep_uris_;
};
/**
* Resolves attributes in the XmlResource and compiles string values to resource
* values.
* Once an XmlResource is processed by this linker, it is ready to be flattened.
*/
// Resolves attributes in the XmlResource and compiles string values to resource values.
// Once an XmlResource is processed by this linker, it is ready to be flattened.
class XmlReferenceLinker : public IXmlResourceConsumer {
public:
XmlReferenceLinker() = default;

View File

@@ -30,23 +30,18 @@
#include "util/Util.h"
#include "xml/XmlUtil.h"
using android::StringPiece;
using ::android::StringPiece;
namespace aapt {
namespace {
/**
* The ReferenceLinkerVisitor will follow all references and make sure they
* point
* to resources that actually exist, either in the local resource table, or as
* external
* symbols. Once the target resource has been found, the ID of the resource will
* be assigned
* to the reference object.
*
* NOTE: All of the entries in the ResourceTable must be assigned IDs.
*/
// The ReferenceLinkerVisitor will follow all references and make sure they point
// to resources that actually exist, either in the local resource table, or as external
// symbols. Once the target resource has been found, the ID of the resource will be assigned
// to the reference object.
//
// NOTE: All of the entries in the ResourceTable must be assigned IDs.
class ReferenceLinkerVisitor : public ValueVisitor {
public:
using ValueVisitor::Visit;
@@ -65,14 +60,9 @@ class ReferenceLinkerVisitor : public ValueVisitor {
}
}
/**
* We visit the Style specially because during this phase, values of
* attributes are
* all RawString values. Now that we are expected to resolve all symbols, we
* can
* lookup the attributes to find out which types are allowed for the
* attributes' values.
*/
// We visit the Style specially because during this phase, values of attributes are
// all RawString values. Now that we are expected to resolve all symbols, we can
// lookup the attributes to find out which types are allowed for the attributes' values.
void Visit(Style* style) override {
if (style->parent) {
Visit(&style->parent.value());
@@ -81,28 +71,21 @@ class ReferenceLinkerVisitor : public ValueVisitor {
for (Style::Entry& entry : style->entries) {
std::string err_str;
// Transform the attribute reference so that it is using the fully
// qualified package
// name. This will also mark the reference as being able to see private
// resources if
// there was a '*' in the reference or if the package came from the
// private namespace.
// Transform the attribute reference so that it is using the fully qualified package
// name. This will also mark the reference as being able to see private resources if
// there was a '*' in the reference or if the package came from the private namespace.
Reference transformed_reference = entry.key;
TransformReferenceFromNamespace(package_decls_,
context_->GetCompilationPackage(),
&transformed_reference);
ResolvePackage(package_decls_, &transformed_reference);
// Find the attribute in the symbol table and check if it is visible from
// this callsite.
// Find the attribute in the symbol table and check if it is visible from this callsite.
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
transformed_reference, callsite_, symbols_, &err_str);
if (symbol) {
// Assign our style key the correct ID.
// The ID may not exist.
// Assign our style key the correct ID. The ID may not exist.
entry.key.id = symbol->id;
// Try to convert the value to a more specific, typed value based on the
// attribute it is set to.
// Try to convert the value to a more specific, typed value based on the attribute it is
// set to.
entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
// Link/resolve the final value (mostly if it's a reference).
@@ -115,8 +98,8 @@ class ReferenceLinkerVisitor : public ValueVisitor {
// The actual type of this item is incompatible with the attribute.
DiagMessage msg(entry.key.GetSource());
// Call the matches method again, this time with a DiagMessage so we
// fill in the actual error message.
// Call the matches method again, this time with a DiagMessage so we fill in the actual
// error message.
symbol->attribute->Matches(*entry.value, &msg);
context_->GetDiagnostics()->Error(msg);
error_ = true;
@@ -125,7 +108,7 @@ class ReferenceLinkerVisitor : public ValueVisitor {
} else {
DiagMessage msg(entry.key.GetSource());
msg << "style attribute '";
ReferenceLinker::WriteResourceName(&msg, entry.key, transformed_reference);
ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
msg << "' " << err_str;
context_->GetDiagnostics()->Error(msg);
error_ = true;
@@ -133,17 +116,15 @@ class ReferenceLinkerVisitor : public ValueVisitor {
}
}
bool HasError() { return error_; }
bool HasError() {
return error_;
}
private:
DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
/**
* Transform a RawString value into a more specific, appropriate value, based
* on the
* Attribute. If a non RawString value is passed in, this is an identity
* transform.
*/
// Transform a RawString value into a more specific, appropriate value, based on the
// Attribute. If a non RawString value is passed in, this is an identity transform.
std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
const Attribute* attr) {
if (RawString* raw_string = ValueCast<RawString>(value.get())) {
@@ -178,11 +159,9 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
public:
EmptyDeclStack() = default;
Maybe<xml::ExtractedPackage> TransformPackageAlias(
const StringPiece& alias,
const StringPiece& local_package) const override {
Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
if (alias.empty()) {
return xml::ExtractedPackage{local_package.to_string(), true /* private */};
return xml::ExtractedPackage{{}, true /*private*/};
}
return {};
}
@@ -191,32 +170,44 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
};
} // namespace
/**
* The symbol is visible if it is public, or if the reference to it is
* requesting private access
* or if the callsite comes from the same package.
*/
bool ReferenceLinker::IsSymbolVisible(const SymbolTable::Symbol& symbol,
const Reference& ref,
const CallSite& callsite) {
if (!symbol.is_public && !ref.private_reference) {
if (ref.name) {
return callsite.resource.package == ref.name.value().package;
} else if (ref.id && symbol.id) {
return ref.id.value().package_id() == symbol.id.value().package_id();
} else {
return false;
}
// The symbol is visible if it is public, or if the reference to it is requesting private access
// or if the callsite comes from the same package.
bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
const CallSite& callsite) {
if (symbol.is_public || ref.private_reference) {
return true;
}
return true;
if (ref.name) {
const ResourceName& name = ref.name.value();
if (name.package.empty()) {
// If the symbol was found, and the package is empty, that means it was found in the local
// scope, which is always visible (private local).
return true;
}
// The symbol is visible if the reference is local to the same package it is defined in.
return callsite.package == name.package;
}
if (ref.id && symbol.id) {
return ref.id.value().package_id() == symbol.id.value().package_id();
}
return false;
}
} // namespace
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols) {
if (reference.name) {
return symbols->FindByName(reference.name.value());
const ResourceName& name = reference.name.value();
if (name.package.empty()) {
// Use the callsite's package name if no package name was defined.
return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
}
return symbols->FindByName(name);
} else if (reference.id) {
return symbols->FindById(reference.id.value());
} else {
@@ -228,7 +219,7 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const R
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error) {
const SymbolTable::Symbol* symbol = ResolveSymbol(reference, symbols);
const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
if (!symbol) {
if (out_error) *out_error = "not found";
return nullptr;
@@ -274,24 +265,62 @@ Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference&
return xml::AaptAttribute(*symbol->attribute, symbol->id);
}
void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
const Reference& orig,
const Reference& transformed) {
void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
CHECK(out_msg != nullptr);
if (!ref.name) {
*out_msg << ref.id.value();
return;
}
if (orig.name) {
*out_msg << orig.name.value();
if (transformed.name.value() != orig.name.value()) {
*out_msg << " (aka " << transformed.name.value() << ")";
}
} else {
*out_msg << orig.id.value();
*out_msg << ref.name.value();
Reference fully_qualified = ref;
xml::ResolvePackage(decls, &fully_qualified);
ResourceName& full_name = fully_qualified.name.value();
if (full_name.package.empty()) {
full_name.package = callsite.package;
}
if (full_name != ref.name.value()) {
*out_msg << " (aka " << full_name << ")";
}
}
void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
const xml::IPackageDeclStack* decls,
DiagMessage* out_msg) {
CHECK(out_msg != nullptr);
if (!ref.name) {
*out_msg << ref.id.value();
return;
}
const ResourceName& ref_name = ref.name.value();
CHECK_EQ(ref_name.type, ResourceType::kAttr);
if (!ref_name.package.empty()) {
*out_msg << ref_name.package << ":";
}
*out_msg << ref_name.entry;
Reference fully_qualified = ref;
xml::ResolvePackage(decls, &fully_qualified);
ResourceName& full_name = fully_qualified.name.value();
if (full_name.package.empty()) {
full_name.package = callsite.package;
}
if (full_name != ref.name.value()) {
*out_msg << " (aka " << full_name.package << ":" << full_name.entry << ")";
}
}
bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
IAaptContext* context, SymbolTable* symbols,
xml::IPackageDeclStack* decls) {
const xml::IPackageDeclStack* decls) {
CHECK(reference != nullptr);
if (!reference->name && !reference->id) {
// This is @null.
@@ -299,7 +328,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
}
Reference transformed_reference = *reference;
TransformReferenceFromNamespace(decls, context->GetCompilationPackage(), &transformed_reference);
xml::ResolvePackage(decls, &transformed_reference);
std::string err_str;
const SymbolTable::Symbol* s =
@@ -314,7 +343,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
DiagMessage error_msg(reference->GetSource());
error_msg << "resource ";
WriteResourceName(&error_msg, *reference, transformed_reference);
WriteResourceName(*reference, callsite, decls, &error_msg);
error_msg << " " << err_str;
context->GetDiagnostics()->Error(error_msg);
return false;
@@ -324,21 +353,24 @@ bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
EmptyDeclStack decl_stack;
bool error = false;
for (auto& package : table->packages) {
// Since we're linking, each package must have a name.
CHECK(!package->name.empty()) << "all packages being linked must have a name";
for (auto& type : package->types) {
for (auto& entry : type->entries) {
// Symbol state information may be lost if there is no value for the
// resource.
if (entry->symbol_status.state != SymbolState::kUndefined &&
entry->values.empty()) {
context->GetDiagnostics()->Error(
DiagMessage(entry->symbol_status.source)
<< "no definition for declared symbol '"
<< ResourceNameRef(package->name, type->type, entry->name)
<< "'");
// First, unmangle the name if necessary.
ResourceName name(package->name, type->type, entry->name);
NameMangler::Unmangle(&name.entry, &name.package);
// Symbol state information may be lost if there is no value for the resource.
if (entry->symbol_status.state != SymbolState::kUndefined && entry->values.empty()) {
context->GetDiagnostics()->Error(DiagMessage(entry->symbol_status.source)
<< "no definition for declared symbol '" << name << "'");
error = true;
}
CallSite callsite = {ResourceNameRef(package->name, type->type, entry->name)};
// The context of this resource is the package in which it is defined.
const CallSite callsite{name.package};
ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
&table->string_pool, &decl_stack);

View File

@@ -29,83 +29,58 @@
namespace aapt {
/**
* Resolves all references to resources in the ResourceTable and assigns them
* IDs.
* The ResourceTable must already have IDs assigned to each resource.
* Once the ResourceTable is processed by this linker, it is ready to be
* flattened.
*/
// Resolves all references to resources in the ResourceTable and assigns them IDs.
// The ResourceTable must already have IDs assigned to each resource.
// Once the ResourceTable is processed by this linker, it is ready to be flattened.
class ReferenceLinker : public IResourceTableConsumer {
public:
ReferenceLinker() = default;
/**
* Returns true if the symbol is visible by the reference and from the
* callsite.
*/
static bool IsSymbolVisible(const SymbolTable::Symbol& symbol,
const Reference& ref, const CallSite& callsite);
// Performs name mangling and looks up the resource in the symbol table. Uses the callsite's
// package if the reference has no package name defined (implicit).
// Returns nullptr if the symbol was not found.
static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
const CallSite& callsite, SymbolTable* symbols);
/**
* Performs name mangling and looks up the resource in the symbol table.
* Returns nullptr if the symbol was not found.
*/
static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference, SymbolTable* symbols);
/**
* Performs name mangling and looks up the resource in the symbol table. If
* the symbol is not visible by the reference at the callsite, nullptr is
* returned. out_error holds the error message.
*/
// Performs name mangling and looks up the resource in the symbol table. If the symbol is not
// visible by the reference at the callsite, nullptr is returned.
// `out_error` holds the error message.
static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error);
/**
* Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is
* an attribute.
* That is, the return value will have a non-null value for
* ISymbolTable::Symbol::attribute.
*/
// Same as ResolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
// That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error);
/**
* Resolves the attribute reference and returns an xml::AaptAttribute if
* successful.
* If resolution fails, outError holds the error message.
*/
// Resolves the attribute reference and returns an xml::AaptAttribute if successful.
// If resolution fails, outError holds the error message.
static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error);
/**
* Writes the resource name to the DiagMessage, using the
* "orig_name (aka <transformed_name>)" syntax.
*/
static void WriteResourceName(DiagMessage* out_msg, const Reference& orig,
const Reference& transformed);
// Writes the resource name to the DiagMessage, using the
// "orig_name (aka <transformed_name>)" syntax.
static void WriteResourceName(const Reference& orig, const CallSite& callsite,
const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
/**
* Transforms the package name of the reference to the fully qualified package
* name using
* the xml::IPackageDeclStack, then mangles and looks up the symbol. If the
* symbol is visible
* to the reference at the callsite, the reference is updated with an ID.
* Returns false on failure, and an error message is logged to the
* IDiagnostics in the context.
*/
// Same as WriteResourceName but omits the 'attr' part.
static void WriteAttributeName(const Reference& ref, const CallSite& callsite,
const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
// Transforms the package name of the reference to the fully qualified package name using
// the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
// to the reference at the callsite, the reference is updated with an ID.
// Returns false on failure, and an error message is logged to the IDiagnostics in the context.
static bool LinkReference(const CallSite& callsite, Reference* reference, IAaptContext* context,
SymbolTable* symbols, xml::IPackageDeclStack* decls);
SymbolTable* symbols, const xml::IPackageDeclStack* decls);
/**
* Links all references in the ResourceTable.
*/
// Links all references in the ResourceTable.
bool Consume(IAaptContext* context, ResourceTable* table) override;
private:

View File

@@ -18,7 +18,9 @@
#include "test/Test.h"
using android::ResTable_map;
using ::android::ResTable_map;
using ::testing::Eq;
using ::testing::IsNull;
using ::testing::NotNull;
namespace aapt {
@@ -263,7 +265,7 @@ TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic)
.Build());
std::string error;
const CallSite call_site{ResourceNameRef("com.app.test", ResourceType::kString, "foo")};
const CallSite call_site{"com.app.test"};
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
*test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
ASSERT_THAT(symbol, NotNull());
@@ -281,7 +283,7 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
.Build());
std::string error;
const CallSite call_site{ResourceNameRef("com.app.ext", ResourceType::kLayout, "foo")};
const CallSite call_site{"com.app.ext"};
EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
*test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
@@ -293,4 +295,27 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
EXPECT_TRUE(error.empty());
}
TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) {
NameMangler mangler(NameManglerPolicy{"com.app.test"});
SymbolTable table(&mangler);
table.AppendSource(test::StaticSymbolSourceBuilder()
.AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000))
.AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
.Build());
const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
CallSite{"com.app.test"}, &table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
&table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
CallSite{"com.app.bad"}, &table),
IsNull());
}
} // namespace aapt

View File

@@ -24,7 +24,7 @@
#include "ValueVisitor.h"
#include "util/Util.h"
using android::StringPiece;
using ::android::StringPiece;
namespace aapt {
@@ -32,27 +32,23 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table,
const TableMergerOptions& options)
: context_(context), master_table_(out_table), options_(options) {
// Create the desired package that all tables will be merged into.
master_package_ = master_table_->CreatePackage(
context_->GetCompilationPackage(), context_->GetPackageId());
master_package_ =
master_table_->CreatePackage(context_->GetCompilationPackage(), context_->GetPackageId());
CHECK(master_package_ != nullptr) << "package name or ID already taken";
}
bool TableMerger::Merge(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
return MergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
bool TableMerger::Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection) {
return MergeImpl(src, table, collection, false /*overlay*/, true /*allow_new*/);
}
bool TableMerger::MergeOverlay(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
return MergeImpl(src, table, collection, true /* overlay */, options_.auto_add_overlay);
return MergeImpl(src, table, collection, true /*overlay*/, options_.auto_add_overlay);
}
/**
* This will merge packages with the same package name (or no package name).
*/
// This will merge packages with the same package name (or no package name).
bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
io::IFileCollection* collection, bool overlay,
bool allow_new) {
io::IFileCollection* collection, bool overlay, bool allow_new) {
bool error = false;
for (auto& package : table->packages) {
// Only merge an empty package or the package we're building.
@@ -62,9 +58,8 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
if (package->name.empty() || context_->GetCompilationPackage() == package->name) {
FileMergeCallback callback;
if (collection) {
callback = [&](const ResourceNameRef& name,
const ConfigDescription& config, FileReference* new_file,
FileReference* old_file) -> bool {
callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
FileReference* new_file, FileReference* old_file) -> bool {
// The old file's path points inside the APK, so we can use it as is.
io::IFile* f = collection->FindFile(*old_file->path);
if (!f) {
@@ -78,45 +73,38 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
};
}
// Merge here. Once the entries are merged and mangled, any references to
// them are still valid. This is because un-mangled references are
// mangled, then looked up at resolution time.
// Also, when linking, we convert references with no package name to use
// the compilation package name.
error |= !DoMerge(src, table, package.get(), false /* mangle */, overlay,
allow_new, callback);
// Merge here. Once the entries are merged and mangled, any references to them are still
// valid. This is because un-mangled references are mangled, then looked up at resolution
// time. Also, when linking, we convert references with no package name to use the compilation
// package name.
error |=
!DoMerge(src, table, package.get(), false /* mangle */, overlay, allow_new, callback);
}
}
return !error;
}
/**
* This will merge and mangle resources from a static library.
*/
bool TableMerger::MergeAndMangle(const Source& src,
const StringPiece& package_name,
ResourceTable* table,
io::IFileCollection* collection) {
// This will merge and mangle resources from a static library.
bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_name,
ResourceTable* table, io::IFileCollection* collection) {
bool error = false;
for (auto& package : table->packages) {
// Warn of packages with an unrelated ID.
if (package_name != package->name) {
context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package "
<< package->name);
context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package " << package->name);
continue;
}
bool mangle = package_name != context_->GetCompilationPackage();
merged_packages_.insert(package->name);
auto callback = [&](
const ResourceNameRef& name, const ConfigDescription& config,
FileReference* new_file, FileReference* old_file) -> bool {
auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
FileReference* new_file, FileReference* old_file) -> bool {
// The old file's path points inside the APK, so we can use it as is.
io::IFile* f = collection->FindFile(*old_file->path);
if (!f) {
context_->GetDiagnostics()->Error(
DiagMessage(src) << "file '" << *old_file->path << "' not found");
context_->GetDiagnostics()->Error(DiagMessage(src)
<< "file '" << *old_file->path << "' not found");
return false;
}
@@ -124,21 +112,18 @@ bool TableMerger::MergeAndMangle(const Source& src,
return true;
};
error |= !DoMerge(src, table, package.get(), mangle, false /* overlay */,
true /* allow new */, callback);
error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/,
callback);
}
return !error;
}
static bool MergeType(IAaptContext* context, const Source& src,
ResourceTableType* dst_type,
static bool MergeType(IAaptContext* context, const Source& src, ResourceTableType* dst_type,
ResourceTableType* src_type) {
if (dst_type->symbol_status.state < src_type->symbol_status.state) {
// The incoming type's visibility is stronger, so we should override
// the visibility.
// The incoming type's visibility is stronger, so we should override the visibility.
if (src_type->symbol_status.state == SymbolState::kPublic) {
// Only copy the ID if the source is public, or else the ID is
// meaningless.
// Only copy the ID if the source is public, or else the ID is meaningless.
dst_type->id = src_type->id;
}
dst_type->symbol_status = std::move(src_type->symbol_status);
@@ -155,14 +140,12 @@ static bool MergeType(IAaptContext* context, const Source& src,
return true;
}
static bool MergeEntry(IAaptContext* context, const Source& src,
ResourceEntry* dst_entry, ResourceEntry* src_entry) {
static bool MergeEntry(IAaptContext* context, const Source& src, ResourceEntry* dst_entry,
ResourceEntry* src_entry) {
if (dst_entry->symbol_status.state < src_entry->symbol_status.state) {
// The incoming type's visibility is stronger, so we should override
// the visibility.
// The incoming type's visibility is stronger, so we should override the visibility.
if (src_entry->symbol_status.state == SymbolState::kPublic) {
// Only copy the ID if the source is public, or else the ID is
// meaningless.
// Only copy the ID if the source is public, or else the ID is meaningless.
dst_entry->id = src_entry->id;
}
dst_entry->symbol_status = std::move(src_entry->symbol_status);
@@ -171,9 +154,8 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
dst_entry->id && src_entry->id &&
dst_entry->id.value() != src_entry->id.value()) {
// Both entries are public and have different IDs.
context->GetDiagnostics()->Error(
DiagMessage(src) << "cannot merge entry '" << src_entry->name
<< "': conflicting public IDs");
context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge entry '" << src_entry->name
<< "': conflicting public IDs");
return false;
}
return true;
@@ -181,12 +163,10 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
// Modified CollisionResolver which will merge Styleables and Styles. Used with overlays.
//
// Styleables are not actual resources, but they are treated as such during the
// compilation phase.
// Styleables are not actual resources, but they are treated as such during the compilation phase.
//
// Styleables and Styles don't simply overlay each other, their definitions merge
// and accumulate. If both values are Styleables/Styles, we just merge them into the
// existing value.
// Styleables and Styles don't simply overlay each other, their definitions merge and accumulate.
// If both values are Styleables/Styles, we just merge them into the existing value.
static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Value* incoming,
StringPool* pool) {
if (Styleable* existing_styleable = ValueCast<Styleable>(existing)) {

View File

@@ -33,81 +33,49 @@
namespace aapt {
struct TableMergerOptions {
/**
* If true, resources in overlays can be added without previously having
* existed.
*/
// If true, resources in overlays can be added without previously having existed.
bool auto_add_overlay = false;
};
/**
* TableMerger takes resource tables and merges all packages within the tables
* that have the same
* package ID.
*
* If a package has a different name, all the entries in that table have their
* names mangled
* to include the package name. This way there are no collisions. In order to do
* this correctly,
* the TableMerger needs to also mangle any FileReference paths. Once these are
* mangled,
* the original source path of the file, along with the new destination path is
* recorded in the
* queue returned from getFileMergeQueue().
*
* Once the merging is complete, a separate process can go collect the files
* from the various
* source APKs and either copy or process their XML and put them in the correct
* location in
* the final APK.
*/
// TableMerger takes resource tables and merges all packages within the tables that have the same
// package ID.
//
// If a package has a different name, all the entries in that table have their names mangled
// to include the package name. This way there are no collisions. In order to do this correctly,
// the TableMerger needs to also mangle any FileReference paths. Once these are mangled, the
// `IFile` pointer in `FileReference` will point to the original file.
//
// Once the merging is complete, a separate phase can go collect the files from the various
// source APKs and either copy or process their XML and put them in the correct location in the
// final APK.
class TableMerger {
public:
/**
* Note: The out_table ResourceTable must live longer than this TableMerger.
* References are made to this ResourceTable for efficiency reasons.
*/
TableMerger(IAaptContext* context, ResourceTable* out_table,
const TableMergerOptions& options);
// Note: The out_table ResourceTable must live longer than this TableMerger.
// References are made to this ResourceTable for efficiency reasons.
TableMerger(IAaptContext* context, ResourceTable* out_table, const TableMergerOptions& options);
const std::set<std::string>& merged_packages() const {
inline const std::set<std::string>& merged_packages() const {
return merged_packages_;
}
/**
* Merges resources from the same or empty package. This is for local sources.
* An io::IFileCollection is optional and used to find the referenced Files
* and process them.
*/
bool Merge(const Source& src, ResourceTable* table,
io::IFileCollection* collection = nullptr);
// Merges resources from the same or empty package. This is for local sources.
// An io::IFileCollection is optional and used to find the referenced Files and process them.
bool Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection = nullptr);
/**
* Merges resources from an overlay ResourceTable.
* An io::IFileCollection is optional and used to find the referenced Files
* and process them.
*/
// Merges resources from an overlay ResourceTable.
// An io::IFileCollection is optional and used to find the referenced Files and process them.
bool MergeOverlay(const Source& src, ResourceTable* table,
io::IFileCollection* collection = nullptr);
/**
* Merges resources from the given package, mangling the name. This is for
* static libraries.
* An io::IFileCollection is needed in order to find the referenced Files and
* process them.
*/
// Merges resources from the given package, mangling the name. This is for static libraries.
// An io::IFileCollection is needed in order to find the referenced Files and process them.
bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table,
io::IFileCollection* collection);
/**
* Merges a compiled file that belongs to this same or empty package. This is
* for local sources.
*/
// Merges a compiled file that belongs to this same or empty package. This is for local sources.
bool MergeFile(const ResourceFile& fileDesc, io::IFile* file);
/**
* Merges a compiled file from an overlay, overriding an existing definition.
*/
// Merges a compiled file from an overlay, overriding an existing definition.
bool MergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
private:

View File

@@ -31,13 +31,9 @@ namespace aapt {
namespace {
/**
* Visits all references (including parents of styles, references in styles,
* arrays, etc) and
* links their symbolic name to their Resource ID, performing mangling and
* package aliasing
* as needed.
*/
// Visits all references (including parents of styles, references in styles, arrays, etc) and
// links their symbolic name to their Resource ID, performing mangling and package aliasing
// as needed.
class ReferenceVisitor : public ValueVisitor {
public:
using ValueVisitor::Visit;
@@ -52,7 +48,9 @@ class ReferenceVisitor : public ValueVisitor {
}
}
bool HasError() const { return error_; }
bool HasError() const {
return error_;
}
private:
DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
@@ -64,9 +62,7 @@ class ReferenceVisitor : public ValueVisitor {
bool error_;
};
/**
* Visits each xml Element and compiles the attributes within.
*/
// Visits each xml Element and compiles the attributes within.
class XmlVisitor : public xml::PackageAwareVisitor {
public:
using xml::PackageAwareVisitor::Visit;
@@ -92,18 +88,12 @@ class XmlVisitor : public xml::PackageAwareVisitor {
// they were assigned to the default Attribute.
const Attribute* attribute = &kDefaultAttribute;
std::string attribute_package;
if (Maybe<xml::ExtractedPackage> maybe_package =
xml::ExtractPackageFromNamespace(attr.namespace_uri)) {
// There is a valid package name for this attribute. We will look this up.
attribute_package = maybe_package.value().package;
if (attribute_package.empty()) {
// Empty package means the 'current' or 'local' package.
attribute_package = context_->GetCompilationPackage();
}
Reference attr_ref(ResourceNameRef(attribute_package, ResourceType::kAttr, attr.name));
Reference attr_ref(
ResourceNameRef(maybe_package.value().package, ResourceType::kAttr, attr.name));
attr_ref.private_reference = maybe_package.value().private_namespace;
std::string err_str;
@@ -111,9 +101,11 @@ class XmlVisitor : public xml::PackageAwareVisitor {
ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
if (!attr.compiled_attribute) {
context_->GetDiagnostics()->Error(DiagMessage(source) << "attribute '"
<< attribute_package << ":"
<< attr.name << "' " << err_str);
DiagMessage error_msg(source);
error_msg << "attribute ";
ReferenceLinker::WriteAttributeName(attr_ref, callsite_, this, &error_msg);
error_msg << " " << err_str;
context_->GetDiagnostics()->Error(error_msg);
error_ = true;
continue;
}
@@ -129,12 +121,8 @@ class XmlVisitor : public xml::PackageAwareVisitor {
} else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
// We won't be able to encode this as a string.
DiagMessage msg(source);
msg << "'" << attr.value << "' "
<< "is incompatible with attribute ";
if (!attribute_package.empty()) {
msg << attribute_package << ":";
}
msg << attr.name << " " << *attribute;
msg << "'" << attr.value << "' is incompatible with attribute " << attr.name << " "
<< *attribute;
context_->GetDiagnostics()->Error(msg);
error_ = true;
}
@@ -163,7 +151,17 @@ class XmlVisitor : public xml::PackageAwareVisitor {
} // namespace
bool XmlReferenceLinker::Consume(IAaptContext* context, xml::XmlResource* resource) {
const CallSite callsite = {resource->file.name};
CallSite callsite{resource->file.name.package};
std::string out_name = resource->file.name.entry;
NameMangler::Unmangle(&out_name, &callsite.package);
if (callsite.package.empty()) {
// Assume an empty package means that the XML file is local. This is true of AndroidManifest.xml
// for example.
callsite.package = context->GetCompilationPackage();
}
XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
if (resource->root) {
resource->root->Accept(&visitor);

View File

@@ -274,6 +274,8 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
switch (code) {
case ResXMLParser::START_NAMESPACE: {
NamespaceDecl decl;
decl.line_number = tree.getLineNumber();
size_t len;
const char16_t* str16 = tree.getNamespacePrefix(&len);
if (str16) {
@@ -288,6 +290,7 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
if (pending_element == nullptr) {
pending_element = util::make_unique<Element>();
}
pending_element->namespace_decls.push_back(std::move(decl));
break;
}
@@ -297,8 +300,8 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
el = std::move(pending_element);
} else {
el = util::make_unique<Element>();
;
}
el->line_number = tree.getLineNumber();
size_t len;
const char16_t* str16 = tree.getElementNamespace(&len);
@@ -479,10 +482,9 @@ void PackageAwareVisitor::AfterVisitElement(Element* el) {
package_decls_.pop_back();
}
Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
const StringPiece& alias, const StringPiece& local_package) const {
Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(const StringPiece& alias) const {
if (alias.empty()) {
return ExtractedPackage{local_package.to_string(), false /* private */};
return ExtractedPackage{{}, false /*private*/};
}
const auto rend = package_decls_.rend();
@@ -493,7 +495,7 @@ Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
const PackageDecl& decl = *iter2;
if (alias == decl.prefix) {
if (decl.package.package.empty()) {
return ExtractedPackage{local_package.to_string(), decl.package.private_namespace};
return ExtractedPackage{{}, decl.package.private_namespace};
}
return decl.package;
}

View File

@@ -185,8 +185,7 @@ class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
public:
using Visitor::Visit;
Maybe<ExtractedPackage> TransformPackageAlias(
const android::StringPiece& alias, const android::StringPiece& local_package) const override;
Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
protected:
PackageAwareVisitor() = default;

View File

@@ -86,19 +86,14 @@ class TestVisitor : public PackageAwareVisitor {
void Visit(Element* el) override {
if (el->name == "View1") {
EXPECT_THAT(TransformPackageAlias("one", "local"),
Eq(make_value(ExtractedPackage{"com.one", false})));
EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
} else if (el->name == "View2") {
EXPECT_THAT(TransformPackageAlias("one", "local"),
Eq(make_value(ExtractedPackage{"com.one", false})));
EXPECT_THAT(TransformPackageAlias("two", "local"),
Eq(make_value(ExtractedPackage{"com.two", false})));
EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
} else if (el->name == "View3") {
EXPECT_THAT(TransformPackageAlias("one", "local"),
Eq(make_value(ExtractedPackage{"com.one", false})));
EXPECT_THAT(TransformPackageAlias("two", "local"),
Eq(make_value(ExtractedPackage{"com.two", false})));
EXPECT_THAT(TransformPackageAlias("three", "local"),
EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
EXPECT_THAT(TransformPackageAlias("three"),
Eq(make_value(ExtractedPackage{"com.three", false})));
}
}
@@ -112,7 +107,6 @@ TEST(XmlDomTest, PackageAwareXmlVisitor) {
</View2>
</View1>)");
Debug::DumpXml(doc.get());
TestVisitor visitor;
doc->root->Accept(&visitor);
}

View File

@@ -141,17 +141,16 @@ const std::string& XmlPullParser::namespace_uri() const {
return event_queue_.front().data2;
}
Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(
const StringPiece& alias, const StringPiece& local_package) const {
Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(const StringPiece& alias) const {
if (alias.empty()) {
return ExtractedPackage{local_package.to_string(), false /* private */};
return ExtractedPackage{{}, false /*private*/};
}
const auto end_iter = package_aliases_.rend();
for (auto iter = package_aliases_.rbegin(); iter != end_iter; ++iter) {
if (alias == iter->prefix) {
if (iter->package.package.empty()) {
return ExtractedPackage{local_package.to_string(), iter->package.private_namespace};
return ExtractedPackage{{}, iter->package.private_namespace};
}
return iter->package;
}

View File

@@ -119,8 +119,7 @@ class XmlPullParser : public IPackageDeclStack {
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
* 'package' will be set to 'defaultPackage'.
*/
Maybe<ExtractedPackage> TransformPackageAlias(
const android::StringPiece& alias, const android::StringPiece& local_package) const override;
Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
//
// Remaining methods are for retrieving information about attributes

View File

@@ -62,19 +62,15 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(
return {};
}
void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
const StringPiece& local_package,
Reference* in_ref) {
void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref) {
if (in_ref->name) {
if (Maybe<ExtractedPackage> transformed_package =
decl_stack->TransformPackageAlias(in_ref->name.value().package,
local_package)) {
decl_stack->TransformPackageAlias(in_ref->name.value().package)) {
ExtractedPackage& extracted_package = transformed_package.value();
in_ref->name.value().package = std::move(extracted_package.package);
// If the reference was already private (with a * prefix) and the
// namespace is public,
// we keep the reference private.
// namespace is public, we keep the reference private.
in_ref->private_reference |= extracted_package.private_namespace;
}
}

View File

@@ -35,7 +35,7 @@ constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
// Result of extracting a package name from a namespace URI declaration.
struct ExtractedPackage {
// The name of the package. This can be the empty string, which means that the package
// should be assumed to be the package being compiled.
// should be assumed to be the same as the CallSite it was defined in.
std::string package;
// True if the package's private namespace was declared. This means that private resources
@@ -51,8 +51,8 @@ struct ExtractedPackage {
// http://schemas.android.com/apk/res/<package> or
// http://schemas.android.com/apk/prv/res/<package>
//
// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
// returns an empty package name.
// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto, returns an empty
// package name.
Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
// Returns an XML Android namespace for the given package of the form:
@@ -63,21 +63,20 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace
std::string BuildPackageNamespace(const android::StringPiece& package,
bool private_reference = false);
// Interface representing a stack of XML namespace declarations. When looking up the package
// for a namespace prefix, the stack is checked from top to bottom.
// Interface representing a stack of XML namespace declarations. When looking up the package for a
// namespace prefix, the stack is checked from top to bottom.
struct IPackageDeclStack {
virtual ~IPackageDeclStack() = default;
// Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
virtual Maybe<ExtractedPackage> TransformPackageAlias(
const android::StringPiece& alias, const android::StringPiece& local_package) const = 0;
const android::StringPiece& alias) const = 0;
};
// Helper function for transforming the original Reference inRef to a fully qualified reference
// via the IPackageDeclStack. This will also mark the Reference as private if the namespace of the
// package declaration was private.
void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
const android::StringPiece& local_package, Reference* in_ref);
void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref);
} // namespace xml
} // namespace aapt