diff options
Diffstat (limited to 'tools')
35 files changed, 817 insertions, 31 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 2ff92e651eda..e6d94e67c3fc 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -111,6 +111,7 @@ cc_library_host_static {          "link/XmlReferenceLinker.cpp",          "optimize/MultiApkGenerator.cpp",          "optimize/ResourceDeduper.cpp", +        "optimize/ResourceFilter.cpp",          "optimize/VersionCollapser.cpp",          "process/SymbolTable.cpp",          "split/TableSplitter.cpp", diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 1b6f8827291b..2260ba4f120f 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -447,6 +447,9 @@ bool ResourceParser::ParseResources(xml::XmlPullParser* parser) {      parsed_resource.config = config_;      parsed_resource.source = source_.WithLine(parser->line_number());      parsed_resource.comment = std::move(comment); +    if (options_.visibility) { +      parsed_resource.visibility_level = options_.visibility.value(); +    }      // Extract the product name if it exists.      if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) { @@ -811,6 +814,12 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser,  }  bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) { +  if (options_.visibility) { +    diag_->Error(DiagMessage(out_resource->source) +                 << "<public> tag not allowed with --visibility flag"); +    return false; +  } +    if (out_resource->config != ConfigDescription::DefaultConfig()) {      diag_->Warn(DiagMessage(out_resource->source)                  << "ignoring configuration '" << out_resource->config << "' for <public> tag"); @@ -853,6 +862,12 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out  }  bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) { +  if (options_.visibility) { +    diag_->Error(DiagMessage(out_resource->source) +                 << "<public-group> tag not allowed with --visibility flag"); +    return false; +  } +    if (out_resource->config != ConfigDescription::DefaultConfig()) {      diag_->Warn(DiagMessage(out_resource->source)                  << "ignoring configuration '" << out_resource->config @@ -974,6 +989,11 @@ bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,  }  bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource) { +  if (options_.visibility) { +    diag_->Error(DiagMessage(out_resource->source) +                 << "<java-symbol> and <symbol> tags not allowed with --visibility flag"); +    return false; +  }    if (out_resource->config != ConfigDescription::DefaultConfig()) {      diag_->Warn(DiagMessage(out_resource->source)                  << "ignoring configuration '" << out_resource->config << "' for <" @@ -1045,6 +1065,9 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource        child_resource.name.entry = maybe_name.value().to_string();        child_resource.source = item_source;        child_resource.overlayable = true; +      if (options_.visibility) { +        child_resource.visibility_level = options_.visibility.value(); +      }        out_resource->child_resources.push_back(std::move(child_resource));        xml::XmlPullParser::SkipCurrentElement(parser); @@ -1187,6 +1210,9 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,          child_resource.name = symbol.symbol.name.value();          child_resource.source = item_source;          child_resource.value = util::make_unique<Id>(); +        if (options_.visibility) { +          child_resource.visibility_level = options_.visibility.value(); +        }          out_resource->child_resources.push_back(std::move(child_resource));          symbol.symbol.SetComment(std::move(comment)); @@ -1564,6 +1590,9 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,        child_resource.name = child_ref.name.value();        child_resource.source = item_source;        child_resource.comment = std::move(comment); +      if (options_.visibility) { +        child_resource.visibility_level = options_.visibility.value(); +      }        if (!ParseAttrImpl(parser, &child_resource, true)) {          error = true; diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h index fb9dbd0cd0fd..68130c2512d3 100644 --- a/tools/aapt2/ResourceParser.h +++ b/tools/aapt2/ResourceParser.h @@ -45,6 +45,10 @@ struct ResourceParserOptions {     * warnings.     */    bool error_on_positional_arguments = true; + +  // If visibility was forced, we need to use it when creating a new resource and also error if we +  // try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags. +  Maybe<Visibility::Level> visibility;  };  /* diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index 2d83a14e541d..1db63350670c 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -114,6 +114,7 @@ struct CompileOptions {    std::string output_path;    Maybe<std::string> res_dir;    Maybe<std::string> generate_text_symbols_path; +  Maybe<Visibility::Level> visibility;    bool pseudolocalize = false;    bool no_png_crunch = false;    bool legacy_mode = false; @@ -216,6 +217,10 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,      // If the filename includes donottranslate, then the default translatable is false.      parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos; +    // If visibility was forced, we need to use it when creating a new resource and also error if +    // we try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags. +    parser_options.visibility = options.visibility; +      ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,                                parser_options);      if (!res_parser.Parse(&xml_parser)) { @@ -309,6 +314,8 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,              if (!entry->values.empty()) {                auto styleable = static_cast<const Styleable*>(entry->values.front()->value.get());                for (const auto& attr : styleable->entries) { +                // The visibility of the children under the styleable does not matter as they are +                // nested under their parent and use its visibility.                  r_txt_printer.Print("default int styleable ");                  r_txt_printer.Print(entry->name);                  r_txt_printer.Print("_"); @@ -677,6 +684,10 @@ class CompileContext : public IAaptContext {      return 0;    } +  bool IsAutoNamespace() override { +    return false; +  } +   private:    DISALLOW_COPY_AND_ASSIGN(CompileContext); @@ -690,6 +701,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {    CompileOptions options;    bool verbose = false; +  Maybe<std::string> visibility;    Flags flags =        Flags()            .RequiredFlag("-o", "Output path", &options.output_path) @@ -705,13 +717,32 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {            .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)            .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",                            &options.legacy_mode) -          .OptionalSwitch("-v", "Enables verbose logging", &verbose); +          .OptionalSwitch("-v", "Enables verbose logging", &verbose) +          .OptionalFlag("--visibility", +                        "Sets the visibility of the compiled resources to the specified\n" +                        "level. Accepted levels: public, private, default", +                        &visibility);    if (!flags.Parse("aapt2 compile", args, &std::cerr)) {      return 1;    }    context.SetVerbose(verbose); +  if (visibility) { +    if (visibility.value() == "public") { +      options.visibility = Visibility::Level::kPublic; +    } else if (visibility.value() == "private") { +      options.visibility = Visibility::Level::kPrivate; +    } else if (visibility.value() == "default") { +      options.visibility = Visibility::Level::kUndefined; +    } else { +      context.GetDiagnostics()->Error( +          DiagMessage() << "Unrecognized visibility level passes to --visibility: '" +                        << visibility.value() << "'. Accepted levels: public, private, default"); +      return 1; +    } +  } +    std::unique_ptr<IArchiveWriter> archive_writer;    std::vector<ResourcePathData> input_data; diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index eb307fb1ddce..3c8beaa19008 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -310,6 +310,10 @@ class Context : public IAaptContext {      return 0u;    } +  bool IsAutoNamespace() override { +    return false; +  } +    bool verbose_ = false;    std::string package_; diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp index 12113ed8a48a..16c7bba33e10 100644 --- a/tools/aapt2/cmd/Diff.cpp +++ b/tools/aapt2/cmd/Diff.cpp @@ -64,6 +64,10 @@ class DiffContext : public IAaptContext {      return 0;    } +  bool IsAutoNamespace() override { +    return false; +  } +   private:    std::string empty_;    StdErrDiagnostics diagnostics_; diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp index 8e7e5e59bc31..fd133f3da175 100644 --- a/tools/aapt2/cmd/Dump.cpp +++ b/tools/aapt2/cmd/Dump.cpp @@ -298,6 +298,10 @@ class DumpContext : public IAaptContext {      return 0;    } +  bool IsAutoNamespace() override { +    return false; +  } +   private:    StdErrDiagnostics diagnostics_;    bool verbose_ = false; diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index a3b766481887..54509d9d0c05 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -17,6 +17,7 @@  #include <sys/stat.h>  #include <cinttypes> +#include <algorithm>  #include <queue>  #include <unordered_map>  #include <vector> @@ -111,6 +112,7 @@ struct LinkOptions {    // Static lib options.    bool no_static_lib_packages = false; +  bool auto_namespace_static_lib = false;    // AndroidManifest.xml massaging options.    ManifestFixerOptions manifest_fixer_options; @@ -193,6 +195,14 @@ class LinkContext : public IAaptContext {      min_sdk_version_ = minSdk;    } +  bool IsAutoNamespace() override { +    return auto_namespace_; +  } + +  void SetAutoNamespace(bool val) { +    auto_namespace_ = val; +  } +   private:    DISALLOW_COPY_AND_ASSIGN(LinkContext); @@ -204,6 +214,7 @@ class LinkContext : public IAaptContext {    SymbolTable symbols_;    bool verbose_ = false;    int min_sdk_version_ = 0; +  bool auto_namespace_ = false;  };  // A custom delegate that generates compatible pre-O IDs for use with feature splits. @@ -2112,6 +2123,10 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {            .OptionalSwitch("--no-static-lib-packages",                            "Merge all library resources under the app's package.",                            &options.no_static_lib_packages) +          .OptionalSwitch("--auto-namespace-static-lib", +                          "Automatically namespace resource references when building a static\n" +                          "library.", +                          &options.auto_namespace_static_lib)            .OptionalSwitch("--non-final-ids",                            "Generates R.java without the final modifier. This is implied when\n"                            "--static-lib is specified.", @@ -2151,6 +2166,8 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {                          &options.manifest_fixer_options.rename_instrumentation_target_package)            .OptionalFlagList("-0", "File extensions not to compress.",                              &options.extensions_to_not_compress) +          .OptionalSwitch("--no-compress", "Do not compress any resources.", +                          &options.do_not_compress_anything)            .OptionalSwitch("--warn-manifest-validation",                            "Treat manifest validation errors as warnings.",                            &options.manifest_fixer_options.warn_validation) @@ -2223,6 +2240,15 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {      options.output_format = OutputFormat::kProto;    } +  if (options.auto_namespace_static_lib) { +    if (!static_lib) { +      context.GetDiagnostics()->Error( +          DiagMessage() << "--auto-namespace-static-lib can only be used with --static-lib"); +      return 1; +    } +    context.SetAutoNamespace(true); +  } +    if (package_id) {      if (context.GetPackageType() != PackageType::kApp) {        context.GetDiagnostics()->Error( diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index 9c76119f9504..4afa8f0a907d 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -38,6 +38,7 @@  #include "io/Util.h"  #include "optimize/MultiApkGenerator.h"  #include "optimize/ResourceDeduper.h" +#include "optimize/ResourceFilter.h"  #include "optimize/VersionCollapser.h"  #include "split/TableSplitter.h"  #include "util/Files.h" @@ -62,6 +63,9 @@ struct OptimizeOptions {    // Details of the app extracted from the AndroidManifest.xml    AppInfo app_info; +  // Blacklist of unused resources that should be removed from the apk. +  std::unordered_set<ResourceName> resources_blacklist; +    // Split APK options.    TableSplitterOptions table_splitter_options; @@ -129,6 +133,10 @@ class OptimizeContext : public IAaptContext {      return sdk_version_;    } +  bool IsAutoNamespace() override { +    return false; +  } +   private:    DISALLOW_COPY_AND_ASSIGN(OptimizeContext); @@ -147,6 +155,13 @@ class OptimizeCommand {      if (context_->IsVerbose()) {        context_->GetDiagnostics()->Note(DiagMessage() << "Optimizing APK...");      } +    if (!options_.resources_blacklist.empty()) { +      ResourceFilter filter(options_.resources_blacklist); +      if (!filter.Consume(context_, apk->GetResourceTable())) { +        context_->GetDiagnostics()->Error(DiagMessage() << "failed filtering resources"); +        return 1; +      } +    }      VersionCollapser collapser;      if (!collapser.Consume(context_, apk->GetResourceTable())) { @@ -284,16 +299,62 @@ class OptimizeCommand {    OptimizeContext* context_;  }; -bool ExtractWhitelistFromConfig(const std::string& path, OptimizeContext* context, -                                OptimizeOptions* options) { +bool ExtractObfuscationWhitelistFromConfig(const std::string& path, OptimizeContext* context, +                                           OptimizeOptions* options) {    std::string contents;    if (!ReadFileToString(path, &contents, true)) {      context->GetDiagnostics()->Error(DiagMessage()                                       << "failed to parse whitelist from config file: " << path);      return false;    } -  for (const StringPiece& resource_name : util::Tokenize(contents, ',')) { -    options->table_flattener_options.whitelisted_resources.insert(resource_name.to_string()); +  for (StringPiece resource_name : util::Tokenize(contents, ',')) { +    options->table_flattener_options.whitelisted_resources.insert( +        resource_name.to_string()); +  } +  return true; +} + +bool ExtractConfig(const std::string& path, OptimizeContext* context, +                                    OptimizeOptions* options) { +  std::string content; +  if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) { +    context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading whitelist"); +    return false; +  } + +  size_t line_no = 0; +  for (StringPiece line : util::Tokenize(content, '\n')) { +    line_no++; +    line = util::TrimWhitespace(line); +    if (line.empty()) { +      continue; +    } + +    auto split_line = util::Split(line, '#'); +    if (split_line.size() < 2) { +      context->GetDiagnostics()->Error(DiagMessage(line) << "No # found in line"); +      return false; +    } +    StringPiece resource_string = split_line[0]; +    StringPiece directives = split_line[1]; +    ResourceNameRef resource_name; +    if (!ResourceUtils::ParseResourceName(resource_string, &resource_name)) { +      context->GetDiagnostics()->Error(DiagMessage(line) << "Malformed resource name"); +      return false; +    } +    if (!resource_name.package.empty()) { +      context->GetDiagnostics()->Error(DiagMessage(line) +                                       << "Package set for resource. Only use type/name"); +      return false; +    } +    for (StringPiece directive : util::Tokenize(directives, ',')) { +      if (directive == "remove") { +        options->resources_blacklist.insert(resource_name.ToResourceName()); +      } else if (directive == "no_obfuscate") { +        options->table_flattener_options.whitelisted_resources.insert( +            resource_name.entry.to_string()); +      } +    }    }    return true;  } @@ -322,6 +383,7 @@ int Optimize(const std::vector<StringPiece>& args) {    OptimizeOptions options;    Maybe<std::string> config_path;    Maybe<std::string> whitelist_path; +  Maybe<std::string> resources_config_path;    Maybe<std::string> target_densities;    std::vector<std::string> configs;    std::vector<std::string> split_args; @@ -340,10 +402,15 @@ int Optimize(const std::vector<StringPiece>& args) {                "All the resources that would be unused on devices of the given densities will be \n"                "removed from the APK.",                &target_densities) -          .OptionalFlag("--whitelist-config-path", +          .OptionalFlag("--whitelist-path",                          "Path to the whitelist.cfg file containing whitelisted resources \n"                          "whose names should not be altered in final resource tables.",                          &whitelist_path) +          .OptionalFlag("--resources-config-path", +                        "Path to the resources.cfg file containing the list of resources and \n" +                        "directives to each resource. \n" +                        "Format: type/resource_name#[directive][,directive]", +                        &resources_config_path)            .OptionalFlagList("-c",                              "Comma separated list of configurations to include. The default\n"                              "is all configurations.", @@ -460,12 +527,19 @@ int Optimize(const std::vector<StringPiece>& args) {    if (options.table_flattener_options.collapse_key_stringpool) {      if (whitelist_path) {        std::string& path = whitelist_path.value(); -      if (!ExtractWhitelistFromConfig(path, &context, &options)) { +      if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options)) {          return 1;        }      }    } +  if (resources_config_path) { +    std::string& path = resources_config_path.value(); +    if (!ExtractConfig(path, &context, &options)) { +      return 1; +    } +  } +    if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {      return 1;    } diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk new file mode 100644 index 000000000000..5d7a6f7bedab --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk @@ -0,0 +1,18 @@ +# +# Copyright (C) 2018 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 $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk new file mode 100644 index 000000000000..91716b9870ea --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk @@ -0,0 +1,29 @@ +# +# Copyright (C) 2018 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_MODULE := AaptTestAutoNamespace_LibOne +LOCAL_MODULE_TAGS := tests +LOCAL_SRC_FILES := $(call all-java-files-under,src) +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res +LOCAL_AAPT_NAMESPACES := true +LOCAL_AAPT_FLAGS := --auto-namespace-static-lib +# We need this to compile the Java sources of AaptTestStaticLib_LibTwo using javac. +LOCAL_JAR_EXCLUDE_FILES := none +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml new file mode 100644 index 000000000000..f585840cdb12 --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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 package="com.example.android.aapt2.autonamespace.staticlib.one" /> diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml new file mode 100644 index 000000000000..3e57b0f20739 --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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> +    <!-- An attribute from StaticLibOne --> +    <attr name="StaticLibOne_attr" format="string" /> + +    <string name="Foo">Foo</string> + +    <declare-styleable name="Widget"> +        <attr name="StaticLibOne_attr" /> +        <attr name="android:text" /> +    </declare-styleable> +</resources> diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java new file mode 100644 index 000000000000..886d48c7597c --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018 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.example.android.aapt2.autonamespace.staticlib.one; + +public class StaticLibOne { public static int FooId = R.string.Foo; } diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk new file mode 100644 index 000000000000..c85496d38fda --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk @@ -0,0 +1,29 @@ +# +# Copyright (C) 2018 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_MODULE := AaptTestAutoNamespace_LibTwo +LOCAL_MODULE_TAGS := tests +LOCAL_SRC_FILES := $(call all-java-files-under,src) +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res +LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestAutoNamespace_LibOne +LOCAL_AAPT_NAMESPACES := true +LOCAL_AAPT_FLAGS := --auto-namespace-static-lib +include $(BUILD_STATIC_JAVA_LIBRARY) + diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml new file mode 100644 index 000000000000..8d3c506c5ac3 --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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 package="com.example.android.aapt2.autonamespace.staticlib.two" /> diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml new file mode 100644 index 000000000000..fb202201a03a --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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. +--> + +<View xmlns:custom="http://schemas.android.com/apk/res-auto" +      custom:StaticLibOne_attr="@string/FooBar" /> diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml new file mode 100644 index 000000000000..c532387ee961 --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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="FooBar">@string/Foo</string> +</resources> diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java new file mode 100644 index 000000000000..323f53ae907b --- /dev/null +++ b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 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.example.android.aapt2.autonamespace.staticlib.two; + +public class StaticLibTwo { +  // IDs from StaticLibOne +  public static int FooId = com.example.android.aapt2.autonamespace.staticlib.one.R.string.Foo; + +  // IDs from StaticLibTwo +  public static int FooBarId = R.string.FooBar; +  public static int LayoutId = R.layout.layout_two; +} diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp index 6b07b1e96261..db1561e17f16 100644 --- a/tools/aapt2/java/JavaClassGenerator.cpp +++ b/tools/aapt2/java/JavaClassGenerator.cpp @@ -347,7 +347,9 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res    }    // Add the Styleable array to the Styleable class. -  out_class_def->AddMember(std::move(array_def)); +  if (out_class_def != nullptr) { +    out_class_def->AddMember(std::move(array_def)); +  }    // Now we emit the indices into the array.    for (size_t i = 0; i < attr_count; i++) { @@ -578,7 +580,6 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,    if (out_r_txt != nullptr) {      r_txt_printer = util::make_unique<Printer>(out_r_txt);    } -    // Generate an onResourcesLoaded() callback if requested.    if (out != nullptr && options_.rewrite_callback_options) {      rewrite_method = diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp index e449546f9399..10a97d84f59d 100644 --- a/tools/aapt2/java/JavaClassGenerator_test.cpp +++ b/tools/aapt2/java/JavaClassGenerator_test.cpp @@ -438,4 +438,22 @@ TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary)    EXPECT_THAT(output, HasSubstr("com.boo.R.onResourcesLoaded"));  } +TEST(JavaClassGeneratorTest, OnlyGenerateRText) { +  std::unique_ptr<ResourceTable> table = +      test::ResourceTableBuilder() +          .SetPackageId("android", 0x01) +          .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>()) +          .AddValue("android:styleable/hey.dude", ResourceId(0x01020000), +                    test::StyleableBuilder() +                        .AddItem("android:attr/foo", ResourceId(0x01010000)) +                        .Build()) +          .Build(); + +  std::unique_ptr<IAaptContext> context = +      test::ContextBuilder().SetPackageId(0x01).SetCompilationPackage("android").Build(); +  JavaClassGenerator generator(context.get(), table.get(), {}); + +  ASSERT_TRUE(generator.Generate("android", nullptr)); +} +  }  // namespace aapt diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp index 9aaaa69f8994..13d2a049e3c4 100644 --- a/tools/aapt2/link/ReferenceLinker.cpp +++ b/tools/aapt2/link/ReferenceLinker.cpp @@ -80,7 +80,7 @@ class ReferenceLinkerVisitor : public DescendingValueVisitor {        // 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); +          transformed_reference, callsite_, symbols_, context_->IsAutoNamespace(), &err_str);        if (symbol) {          // Assign our style key the correct ID. The ID may not exist.          entry.key.id = symbol->id; @@ -202,12 +202,18 @@ bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,  const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,                                                            const CallSite& callsite, -                                                          SymbolTable* symbols) { +                                                          SymbolTable* symbols, +                                                          bool auto_namespace) {    if (reference.name) {      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)); +      const SymbolTable::Symbol* local_symbol = +          symbols->FindByName(ResourceName(callsite.package, name.type, name.entry)); +      if (!auto_namespace || local_symbol) { +        return local_symbol; +      } +      return symbols->FindByNameInAnyPackage(name);      }      return symbols->FindByName(name);    } else if (reference.id) { @@ -220,8 +226,9 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& refer  const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,                                                                           const CallSite& callsite,                                                                           SymbolTable* symbols, +                                                                         bool auto_namespace,                                                                           std::string* out_error) { -  const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols); +  const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols, auto_namespace);    if (!symbol) {      if (out_error) *out_error = "not found";      return nullptr; @@ -235,10 +242,10 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const R  }  const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility( -    const Reference& reference, const CallSite& callsite, SymbolTable* symbols, +    const Reference& reference, const CallSite& callsite, SymbolTable* symbols, bool auto_namespace,      std::string* out_error) {    const SymbolTable::Symbol* symbol = -      ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error); +      ResolveSymbolCheckVisibility(reference, callsite, symbols, auto_namespace, out_error);    if (!symbol) {      return nullptr;    } @@ -253,9 +260,10 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(  Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,                                                                 const CallSite& callsite,                                                                 SymbolTable* symbols, +                                                               bool auto_namespace,                                                                 std::string* out_error) {    const SymbolTable::Symbol* symbol = -      ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error); +      ResolveAttributeCheckVisibility(reference, callsite, symbols, auto_namespace, out_error);    if (!symbol) {      return {};    } @@ -333,8 +341,8 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen    xml::ResolvePackage(decls, &transformed_reference);    std::string err_str; -  const SymbolTable::Symbol* s = -      ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str); +  const SymbolTable::Symbol* s = ResolveSymbolCheckVisibility( +      transformed_reference, callsite, symbols, context->IsAutoNamespace(), &err_str);    if (s) {      // The ID may not exist. This is fine because of the possibility of building      // against libraries without assigned IDs. diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h index b0b49457e5dd..7887915a7bb1 100644 --- a/tools/aapt2/link/ReferenceLinker.h +++ b/tools/aapt2/link/ReferenceLinker.h @@ -36,10 +36,12 @@ class ReferenceLinker : public IResourceTableConsumer {    ReferenceLinker() = default;    // 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). +  // package if the reference has no package name defined (implicit), or if auto_namespace is +  // set try looking in all avaliable packages for a symbol of that name.    // Returns nullptr if the symbol was not found.    static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference, -                                                  const CallSite& callsite, SymbolTable* symbols); +                                                  const CallSite& callsite, SymbolTable* symbols, +                                                  bool auto_namespace);    // 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. @@ -47,6 +49,7 @@ class ReferenceLinker : public IResourceTableConsumer {    static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,                                                                   const CallSite& callsite,                                                                   SymbolTable* symbols, +                                                                 bool auto_namespace,                                                                   std::string* out_error);    // Same as ResolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute. @@ -54,13 +57,14 @@ class ReferenceLinker : public IResourceTableConsumer {    static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,                                                                      const CallSite& callsite,                                                                      SymbolTable* symbols, +                                                                    bool auto_namespace,                                                                      std::string* out_error);    // 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, +                                                       SymbolTable* symbols, bool auto_namespace,                                                         std::string* out_error);    // Writes the resource name to the DiagMessage, using the diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp index be38b967c986..0b7b1ce03a52 100644 --- a/tools/aapt2/link/ReferenceLinker_test.cpp +++ b/tools/aapt2/link/ReferenceLinker_test.cpp @@ -267,7 +267,7 @@ TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic)    std::string error;    const CallSite call_site{"com.app.test"};    const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility( -      *test::BuildReference("com.app.test:string/foo"), call_site, &table, &error); +      *test::BuildReference("com.app.test:string/foo"), call_site, &table, false, &error);    ASSERT_THAT(symbol, NotNull());    EXPECT_TRUE(error.empty());  } @@ -285,13 +285,13 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)    std::string error;    const CallSite call_site{"com.app.ext"}; -  EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute( -      *test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error)); +  EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(*test::BuildReference("com.app.test:attr/foo"), +                                                    call_site, &table, false, &error));    EXPECT_FALSE(error.empty());    error = "";    ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute( -      *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error)); +      *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, false, &error));    EXPECT_TRUE(error.empty());  } @@ -303,19 +303,74 @@ TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) {                           .AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))                           .Build()); -  const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), -                                                                CallSite{"com.app.test"}, &table); +  const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol( +      *test::BuildReference("string/foo"), CallSite{"com.app.test"}, &table, false);    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); +                                     &table, false);    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), +                                             CallSite{"com.app.bad"}, &table, false),                IsNull());  } +TEST(ReferenceLinkerTest, AutomaticNamespace) { +  NameMangler mangler(NameManglerPolicy{"com.example.thislib"}); +  SymbolTable table(&mangler); +  table.AppendSource( +      test::StaticSymbolSourceBuilder() +          .AddSymbol("com.example.thislib:string/thislib_string", ResourceId(0x7f010006)) +          .AddSymbol("com.example.thislib:string/explicit_override_string", ResourceId(0x7f010007)) +          .Build()); +  // Lib2 is higher priority than lib1 +  table.AppendSource( +      test::StaticSymbolSourceBuilder() +          .AddSymbol("com.example.lib2:string/lib2_string", ResourceId(0x7f010003)) +          .AddSymbol("com.example.lib2:string/explicit_override_string", ResourceId(0x7f010004)) +          .AddSymbol("com.example.lib2:string/implicit_override_string", ResourceId(0x7f010005)) +          .Build()); +  table.AppendSource( +      test::StaticSymbolSourceBuilder() +          .AddSymbol("com.example.lib1:string/explicit_override_string", ResourceId(0x7f010001)) +          .AddSymbol("com.example.lib1:string/implicit_override_string", ResourceId(0x7f010002)) +          .Build()); + +  // Sanity test: Local references are still fine. +  const SymbolTable::Symbol* s = +      ReferenceLinker::ResolveSymbol(*test::BuildReference("string/thislib_string"), +                                     CallSite{"com.example.thislib"}, &table, true); +  ASSERT_THAT(s, NotNull()); +  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010006))); + +  // Local references are fine, even if clash with remote ones. +  s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/explicit_override_string"), +                                     CallSite{"com.example.thislib"}, &table, true); +  ASSERT_THAT(s, NotNull()); +  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010007))); + +  // An unqualified reference to lib2 is rewritten +  s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/lib2_string"), +                                     CallSite{"com.example.thislib"}, &table, true); +  ASSERT_THAT(s, NotNull()); +  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010003))); + +  // Qualified references are left alone. +  s = ReferenceLinker::ResolveSymbol( +      *test::BuildReference("com.example.lib2:string/explicit_override_string"), +      CallSite{"com.example.thislib"}, &table, true); +  ASSERT_THAT(s, NotNull()); +  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010004))); + +  // Implicit overrides respect priority ordering. +  s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/implicit_override_string"), +                                     CallSite{"com.example.thislib"}, &table, true); +  ASSERT_THAT(s, NotNull()); +  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010005))); + +  // +}  }  // namespace aapt diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp index 160ff925f6cc..420a1271479d 100644 --- a/tools/aapt2/link/XmlReferenceLinker.cpp +++ b/tools/aapt2/link/XmlReferenceLinker.cpp @@ -97,8 +97,8 @@ class XmlVisitor : public xml::PackageAwareVisitor {          attr_ref.private_reference = maybe_package.value().private_namespace;          std::string err_str; -        attr.compiled_attribute = -            ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str); +        attr.compiled_attribute = ReferenceLinker::CompileXmlAttribute( +            attr_ref, callsite_, symbols_, context_->IsAutoNamespace(), &err_str);          if (!attr.compiled_attribute) {            DiagMessage error_msg(source); diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp index ef99355e5b5f..d321f8f76d87 100644 --- a/tools/aapt2/link/XmlReferenceLinker_test.cpp +++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp @@ -18,6 +18,7 @@  #include "test/Test.h" +using ::testing::Eq;  using ::testing::IsNull;  using ::testing::NotNull; @@ -70,12 +71,29 @@ class XmlReferenceLinkerTest : public ::testing::Test {                                                  .Build())                             .AddPublicSymbol("com.app.test:attr/attr", ResourceId(0x7f010002),                                              test::AttributeBuilder().Build()) +                           .AddPublicSymbol("com.app.lib:string/lib_string", ResourceId(0x7f020003))                             .Build())                     .Build(); + +    auto_namespace_context_ = +        test::ContextBuilder() +            .SetCompilationPackage("com.app.test") +            .SetNameManglerPolicy(NameManglerPolicy{"com.app.test", {"com.android.support"}}) +            .SetAutoNamespace(true) +            .AddSymbolSource( +                test::StaticSymbolSourceBuilder() +                    .AddPublicSymbol("android:attr/text", ResourceId(0x01010003), +                                     test::AttributeBuilder() +                                         .SetTypeMask(android::ResTable_map::TYPE_STRING) +                                         .Build()) +                    .AddPublicSymbol("com.app.lib:string/lib_string", ResourceId(0x7f020003)) +                    .Build()) +            .Build();    }   protected:    std::unique_ptr<IAaptContext> context_; +  std::unique_ptr<IAaptContext> auto_namespace_context_;  };  TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) { @@ -195,6 +213,31 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {    EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);  } +TEST_F(XmlReferenceLinkerTest, LinkAutoNamespaceResReference) { +  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"( +      <View +          xmlns:android="http://schemas.android.com/apk/res/android" +          android:text="@string/lib_string" />)"); + +  XmlReferenceLinker linker; +  // Should not link with auto-namespace support disabled. +  ASSERT_FALSE(linker.Consume(context_.get(), doc.get())); +  // Should link with auto-namespace enabled. +  ASSERT_TRUE(linker.Consume(auto_namespace_context_.get(), doc.get())); + +  xml::Element* view_el = doc->root.get(); +  ASSERT_THAT(view_el, NotNull()); + +  xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text"); +  ASSERT_THAT(xml_attr, NotNull()); +  ASSERT_TRUE(xml_attr->compiled_attribute); +  EXPECT_EQ(make_value(ResourceId(0x01010003)), xml_attr->compiled_attribute.value().id); +  Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get()); +  ASSERT_THAT(ref, NotNull()); +  ASSERT_TRUE(ref->name); +  EXPECT_EQ(make_value(ResourceId(0x7f020003)), ref->id); +} +  TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {    std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(        <View xmlns:app="http://schemas.android.com/apk/res/android" app:attr="@app:id/id"> diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp index 588b3316e6fa..9cfd7300df3d 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator.cpp @@ -98,6 +98,10 @@ class ContextWrapper : public IAaptContext {          util::make_unique<SourcePathDiagnostics>(Source{source}, context_->GetDiagnostics());    } +  bool IsAutoNamespace() override { +    return context_->IsAutoNamespace(); +  } +   private:    IAaptContext* context_;    std::unique_ptr<SourcePathDiagnostics> source_diag_; diff --git a/tools/aapt2/optimize/ResourceFilter.cpp b/tools/aapt2/optimize/ResourceFilter.cpp new file mode 100644 index 000000000000..250b65197a7d --- /dev/null +++ b/tools/aapt2/optimize/ResourceFilter.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 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. + */ + +#include "optimize/ResourceFilter.h" + +#include "ResourceTable.h" + +namespace aapt { + +ResourceFilter::ResourceFilter(const std::unordered_set<ResourceName>& blacklist) +    : blacklist_(blacklist) { +} + +bool ResourceFilter::Consume(IAaptContext* context, ResourceTable* table) { +  for (auto& package : table->packages) { +    for (auto& type : package->types) { +      for (auto it = type->entries.begin(); it != type->entries.end(); ) { +        ResourceName resource = ResourceName({}, type->type, (*it)->name); +        if (blacklist_.find(resource) != blacklist_.end()) { +          it = type->entries.erase(it); +        } else { +          ++it; +        } +      } +    } +  } +  return true; +} + +}  // namespace aapt diff --git a/tools/aapt2/optimize/ResourceFilter.h b/tools/aapt2/optimize/ResourceFilter.h new file mode 100644 index 000000000000..d4baf654b0ff --- /dev/null +++ b/tools/aapt2/optimize/ResourceFilter.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 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. + */ + +#ifndef AAPT_OPTIMIZE_RESOURCEFILTER_H +#define AAPT_OPTIMIZE_RESOURCEFILTER_H + +#include "android-base/macros.h" + +#include "process/IResourceTableConsumer.h" + +#include <unordered_set> + +namespace aapt { + +// Removes non-whitelisted entries from resource table. +class ResourceFilter : public IResourceTableConsumer { + public: +  explicit ResourceFilter(const std::unordered_set<ResourceName>& blacklist); + +  bool Consume(IAaptContext* context, ResourceTable* table) override; + + private: +  DISALLOW_COPY_AND_ASSIGN(ResourceFilter); +  std::unordered_set<ResourceName> blacklist_; +}; + +} // namespace aapt + +#endif  // AAPT_OPTIMIZE_RESOURCEFILTER_H diff --git a/tools/aapt2/optimize/ResourceFilter_test.cpp b/tools/aapt2/optimize/ResourceFilter_test.cpp new file mode 100644 index 000000000000..800b2bfd0403 --- /dev/null +++ b/tools/aapt2/optimize/ResourceFilter_test.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2018 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. + */ + +#include "optimize/ResourceFilter.h" + +#include "ResourceTable.h" +#include "test/Test.h" + +using ::aapt::test::HasValue; +using ::testing::Not; + +namespace aapt { + +TEST(ResourceFilterTest, SomeValuesAreFilteredOut) { +  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); +  const ConfigDescription default_config = {}; + +  std::unique_ptr<ResourceTable> table = +      test::ResourceTableBuilder() +          .AddString("android:string/notblacklisted", ResourceId{}, default_config, "value") +          .AddString("android:string/blacklisted", ResourceId{}, default_config, "value") +          .AddString("android:string/notblacklisted2", ResourceId{}, default_config, "value") +          .AddString("android:string/blacklisted2", ResourceId{}, default_config, "value") +          .Build(); + +  std::unordered_set<ResourceName> blacklist = { +    ResourceName({}, ResourceType::kString, "blacklisted"), +    ResourceName({}, ResourceType::kString, "blacklisted2"), +  }; + +  ASSERT_TRUE(ResourceFilter(blacklist).Consume(context.get(), table.get())); +  EXPECT_THAT(table, HasValue("android:string/notblacklisted", default_config)); +  EXPECT_THAT(table, HasValue("android:string/notblacklisted2", default_config)); +  EXPECT_THAT(table, Not(HasValue("android:string/blacklisted", default_config))); +  EXPECT_THAT(table, Not(HasValue("android:string/blacklisted2", default_config))); +} + +TEST(ResourceFilterTest, TypeIsCheckedBeforeFiltering) { +  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); +  const ConfigDescription default_config = {}; + +  std::unique_ptr<ResourceTable> table = +      test::ResourceTableBuilder() +          .AddString("android:string/notblacklisted", ResourceId{}, default_config, "value") +          .AddString("android:string/blacklisted", ResourceId{}, default_config, "value") +          .AddString("android:drawable/notblacklisted", ResourceId{}, default_config, "value") +          .AddString("android:drawable/blacklisted", ResourceId{}, default_config, "value") +          .Build(); + +  std::unordered_set<ResourceName> blacklist = { +    ResourceName({}, ResourceType::kString, "blacklisted"), +  }; + +  ASSERT_TRUE(ResourceFilter(blacklist).Consume(context.get(), table.get())); +  EXPECT_THAT(table, HasValue("android:string/notblacklisted", default_config)); +  EXPECT_THAT(table, HasValue("android:drawable/blacklisted", default_config)); +  EXPECT_THAT(table, HasValue("android:drawable/notblacklisted", default_config)); +  EXPECT_THAT(table, Not(HasValue("android:string/blacklisted", default_config))); +} + +}  // namespace aapt diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h index 30dad8025900..a3a7719bfe16 100644 --- a/tools/aapt2/process/IResourceTableConsumer.h +++ b/tools/aapt2/process/IResourceTableConsumer.h @@ -50,6 +50,7 @@ struct IAaptContext {    virtual NameMangler* GetNameMangler() = 0;    virtual bool IsVerbose() = 0;    virtual int GetMinSdkVersion() = 0; +  virtual bool IsAutoNamespace() = 0;  };  struct IResourceTableConsumer { diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index 2e97a2f3ae7f..f6f0a50340b3 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -114,6 +114,16 @@ const SymbolTable::Symbol* SymbolTable::FindByName(const ResourceName& name) {    return shared_symbol.get();  } +const SymbolTable::Symbol* SymbolTable::FindByNameInAnyPackage(const ResourceName& name) { +  for (auto& source : sources_) { +    std::string package = source->GetPackageForSymbol(name); +    if (!package.empty()) { +      return FindByName(ResourceName(package, name.type, name.entry)); +    } +  } +  return {}; +} +  const SymbolTable::Symbol* SymbolTable::FindById(const ResourceId& id) {    if (const std::shared_ptr<Symbol>& s = id_cache_.get(id)) {      return s.get(); @@ -211,6 +221,25 @@ std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::FindByName(    return symbol;  } +std::string ResourceTableSymbolSource::GetPackageForSymbol(const ResourceName& name) { +  for (auto& package : table_->packages) { +    ResourceTableType* type = package->FindType(name.type); +    if (type == nullptr) { +      continue; +    } +    ResourceEntry* entry = type->FindEntry(name.entry); +    if (entry == nullptr) { +      continue; +    } +    return package->name; +  } +  if (name.type == ResourceType::kAttr) { +    // Recurse and try looking up a private attribute. +    return GetPackageForSymbol(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry)); +  } +  return {}; +} +  bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {    int32_t cookie = 0;    return assets_.addAssetPath(android::String8(path.data(), path.size()), &cookie); diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h index b676efb8d025..c80e62729e26 100644 --- a/tools/aapt2/process/SymbolTable.h +++ b/tools/aapt2/process/SymbolTable.h @@ -88,6 +88,13 @@ class SymbolTable {    // results are stored in a cache which may evict entries on subsequent calls.    const Symbol* FindByName(const ResourceName& name); +  // Finds the symbol from any package, for use as part of automatic conversion to namespaces. +  // This returns the symbol from the highest priority package, +  // which mimics the behavior of the resource merger and overlays. +  // NOTE: Never hold on to the result between calls to FindByXXX. The +  // results are stored in a cache which may evict entries on subsequent calls. +  const Symbol* FindByNameInAnyPackage(const ResourceName& name); +    // NOTE: Never hold on to the result between calls to FindByXXX. The    // results are stored in a cache which may evict entries on subsequent calls.    const Symbol* FindById(const ResourceId& id); @@ -152,6 +159,11 @@ class ISymbolSource {    virtual std::unique_ptr<SymbolTable::Symbol> FindByName(        const ResourceName& name) = 0; +  // Finds the name of a symbol from any package, +  // for use as part of automatic conversion to namespaces. +  // This returns the symbol from the highest priority package, +  // which mimics the behavior of the resource merger and overlays. +  virtual std::string GetPackageForSymbol(const ResourceName& name) = 0;    virtual std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) = 0;    // Default implementation tries the name if it exists, else the ID. @@ -176,6 +188,7 @@ class ResourceTableSymbolSource : public ISymbolSource {    std::unique_ptr<SymbolTable::Symbol> FindByName(        const ResourceName& name) override; +  std::string GetPackageForSymbol(const ResourceName& name) override;    std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {      return {};    } @@ -195,6 +208,9 @@ class AssetManagerSymbolSource : public ISymbolSource {    std::unique_ptr<SymbolTable::Symbol> FindByName(        const ResourceName& name) override; +  std::string GetPackageForSymbol(const ResourceName& name) override { +    return {}; +  }    std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override;    std::unique_ptr<SymbolTable::Symbol> FindByReference(        const Reference& ref) override; diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp index 1f59d7034300..df40b26a64f4 100644 --- a/tools/aapt2/process/SymbolTable_test.cpp +++ b/tools/aapt2/process/SymbolTable_test.cpp @@ -120,4 +120,39 @@ TEST(SymbolTableTest, FindByNameWhenSymbolIsMangledInResTable) {    EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.other:id/foo")), IsNull());  } +TEST(SymbolTableTest, FindByNameInAnyPackage) { +  // This represents lib3 --depends-on--> lib2 --depends-on--> lib1 + +  NameMangler mangler(NameManglerPolicy{"com.example.lib3"}); +  SymbolTable symbol_table(&mangler); +  // Lib2 has higher precedence than lib1, as it is closer to the current library (lib3) +  // in the dependency graph. + +  symbol_table.AppendSource(test::StaticSymbolSourceBuilder() +                                .AddPublicSymbol("com.example.lib1:string/foo", ResourceId()) +                                .AddSymbol("com.example.lib1:attr/foo", ResourceId(), +                                           test::AttributeBuilder() +                                               .SetTypeMask(android::ResTable_map::TYPE_FLAGS) +                                               .AddItem("one", 0x01) +                                               .AddItem("two", 0x02) +                                               .Build()) +                                .Build()); +  symbol_table.PrependSource(test::StaticSymbolSourceBuilder() +                                 .AddPublicSymbol("com.example.lib2:string/foo", ResourceId()) +                                 .Build()); + +  // Sanity test +  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("string/foo")), IsNull()); + +  // Test public symbol resolution +  const SymbolTable::Symbol* const found_string = +      symbol_table.FindByNameInAnyPackage(test::ParseNameOrDie("string/foo")); +  ASSERT_THAT(found_string, NotNull()); + +  // Test attr resolution +  const SymbolTable::Symbol* const found_attr = +      symbol_table.FindByNameInAnyPackage(test::ParseNameOrDie("attr/foo")); +  ASSERT_THAT(found_attr, NotNull()); +} +  }  // namespace aapt diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h index 0564db063b9a..a07d79f01dfe 100644 --- a/tools/aapt2/test/Context.h +++ b/tools/aapt2/test/Context.h @@ -81,6 +81,10 @@ class Context : public IAaptContext {      return min_sdk_version_;    } +  bool IsAutoNamespace() override { +    return auto_namespace_; +  } +   private:    DISALLOW_COPY_AND_ASSIGN(Context); @@ -93,6 +97,7 @@ class Context : public IAaptContext {    NameMangler name_mangler_;    SymbolTable symbols_;    int min_sdk_version_; +  bool auto_namespace_;  };  class ContextBuilder { @@ -127,6 +132,11 @@ class ContextBuilder {      return *this;    } +  ContextBuilder& SetAutoNamespace(bool auto_namespace) { +    context_->auto_namespace_ = auto_namespace; +    return *this; +  } +    std::unique_ptr<Context> Build() { return std::move(context_); }   private: @@ -172,6 +182,15 @@ class StaticSymbolSourceBuilder {        return nullptr;      } +    std::string GetPackageForSymbol(const ResourceName& name) override { +      for (auto const& imap : name_map_) { +        if (imap.first.type == name.type && imap.first.entry == name.entry) { +          return imap.first.package; +        } +      } +      return ""; +    } +      std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {        auto iter = id_map_.find(id);        if (iter != id_map_.end()) {  |