AAPT2: Automatic Static Library Namespacing.

Introduces a link flag --auto-namespace-static-lib for use when linking
static libraries.

When linking a static library with compiled sources that have references
to resources in provided libraries without an explicit package name,
the flag enables automatic inference of the package.

If a resource is present in the package that is being compiled, that is
used, otherwise the reference is rewritten to the highest precedence
resource with matching name and type.

Test: m out/host/linux-x86/nativetest64/aapt2_tests/aapt2_tests && \
      $ANDROID_HOST_OUT/nativetest64/aapt2_tests/aapt2_tests
Test: m frameworks/base/tools/aapt2/integration-tests
Change-Id: I6c6017e054654d1f60782d0a428a7a2a47f8952b
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 101f74e..06e84ef 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -677,6 +677,10 @@
     return 0;
   }
 
+  bool IsAutoNamespace() override {
+    return false;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(CompileContext);
 
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 7f956c5..56bf487 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -310,6 +310,10 @@
     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 12113ed..16c7bba 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -64,6 +64,10 @@
     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 8e7e5e5..fd133f3 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -298,6 +298,10 @@
     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 69bac04..163a526 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -111,6 +111,7 @@
 
   // 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 +194,14 @@
     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 +213,7 @@
   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 +2122,10 @@
           .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.",
@@ -2152,7 +2166,7 @@
           .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)
+                          &options.do_not_compress_anything)
           .OptionalSwitch("--warn-manifest-validation",
                           "Treat manifest validation errors as warnings.",
                           &options.manifest_fixer_options.warn_validation)
@@ -2221,6 +2235,15 @@
     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 9c76119..da32010 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -129,6 +129,10 @@
     return sdk_version_;
   }
 
+  bool IsAutoNamespace() override {
+    return false;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
 
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
new file mode 100644
index 0000000..5d7a6f7
--- /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 0000000..91716b9
--- /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 0000000..f585840
--- /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 0000000..3e57b0f
--- /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 0000000..886d48c
--- /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 0000000..c85496d
--- /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 0000000..8d3c506
--- /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 0000000..fb20220
--- /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 0000000..c532387
--- /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 0000000..323f53a
--- /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/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 9aaaa69..13d2a04 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -80,7 +80,7 @@
 
       // 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 @@
 
 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::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::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 @@
 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 @@
   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 b0b4945..7887915 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -36,10 +36,12 @@
   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 @@
   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 @@
   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 be38b96..0b7b1ce 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -267,7 +267,7 @@
   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 @@
   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 @@
                          .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 160ff92..420a127 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -97,8 +97,8 @@
         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 ef99355..d321f8f 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 @@
                                                 .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 @@
   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 588b331..9cfd730 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -98,6 +98,10 @@
         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/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index 30dad802..a3a7719 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -50,6 +50,7 @@
   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 2e97a2f..f6f0a50 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -114,6 +114,16 @@
   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 @@
   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 b676efb..c80e627 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -88,6 +88,13 @@
   // 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 @@
 
   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 @@
   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 @@
 
   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 1f59d70..df40b26 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -120,4 +120,39 @@
   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 0564db0..a07d79f 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -81,6 +81,10 @@
     return min_sdk_version_;
   }
 
+  bool IsAutoNamespace() override {
+    return auto_namespace_;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Context);
 
@@ -93,6 +97,7 @@
   NameMangler name_mangler_;
   SymbolTable symbols_;
   int min_sdk_version_;
+  bool auto_namespace_;
 };
 
 class ContextBuilder {
@@ -127,6 +132,11 @@
     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 @@
       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()) {