Allows features to link to other feature splits without namespacing.

Add uses-split dependencies to AppInfo. At link time, this is used
and allows features to reference other features, before
resource namespacing is implemented in Android Studio.

bug:135681292
Test: Link_test, ReferenceLinker_test, and integration tests.
Change-Id: Ifdf0067e7370552b6b9d4d6d4713d4484b6ea154
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 9b81369f..c7ac438 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -629,6 +629,12 @@
     return 0;
   }
 
+  const std::set<std::string>& GetSplitNameDependencies() override {
+    UNIMPLEMENTED(FATAL) << "No Split Name Dependencies be needed in compile phase";
+    static std::set<std::string> empty;
+    return empty;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(CompileContext);
 
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 0cf86cc..22bcd85 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -243,6 +243,12 @@
     return 0u;
   }
 
+  const std::set<std::string>& GetSplitNameDependencies() override {
+    UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+    static std::set<std::string> empty;
+    return empty;
+  }
+
   bool verbose_ = false;
   std::string package_;
 
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 262f4fc4e..d56994e 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -65,6 +65,12 @@
     return 0;
   }
 
+  const std::set<std::string>& GetSplitNameDependencies() override {
+    UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+    static std::set<std::string> empty;
+    return empty;
+  }
+
  private:
   std::string empty_;
   StdErrDiagnostics diagnostics_;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index a23a6a4..429aff1 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -118,6 +118,12 @@
     return 0;
   }
 
+  const std::set<std::string>& GetSplitNameDependencies() override {
+    UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+    static std::set<std::string> empty;
+    return empty;
+  }
+
  private:
   StdErrDiagnostics diagnostics_;
   bool verbose_ = false;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index b8be21c..ed2d0c1 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -140,6 +140,14 @@
     min_sdk_version_ = minSdk;
   }
 
+  const std::set<std::string>& GetSplitNameDependencies() override {
+    return split_name_dependencies_;
+  }
+
+  void SetSplitNameDependencies(const std::set<std::string>& split_name_dependencies) {
+    split_name_dependencies_ = split_name_dependencies;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(LinkContext);
 
@@ -151,6 +159,7 @@
   SymbolTable symbols_;
   bool verbose_ = false;
   int min_sdk_version_ = 0;
+  std::set<std::string> split_name_dependencies_;
 };
 
 // A custom delegate that generates compatible pre-O IDs for use with feature splits.
@@ -963,6 +972,17 @@
         app_info.min_sdk_version = ResourceUtils::ParseSdkVersion(min_sdk->value);
       }
     }
+
+    for (const xml::Element* child_el : manifest_el->GetChildElements()) {
+      if (child_el->namespace_uri.empty() && child_el->name == "uses-split") {
+        if (const xml::Attribute* split_name =
+            child_el->FindAttribute(xml::kSchemaAndroid, "name")) {
+          if (!split_name->value.empty()) {
+            app_info.split_name_dependencies.insert(split_name->value);
+          }
+        }
+      }
+    }
     return app_info;
   }
 
@@ -1770,6 +1790,7 @@
     context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0));
 
     context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
+    context_->SetSplitNameDependencies(app_info_.split_name_dependencies);
 
     // Override the package ID when it is "android".
     if (context_->GetCompilationPackage() == "android") {
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index bf8f043..062dd8e 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "AppInfo.h"
 #include "Link.h"
 
 #include "LoadedApk.h"
@@ -253,4 +254,67 @@
   EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4);  // android:background
 }
 
+TEST_F(LinkTest, AppInfoWithUsesSplit) {
+  StdErrDiagnostics diag;
+  const std::string base_files_dir = GetTestPath("base");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+                          R"(<resources>
+                               <string name="bar">bar</string>
+                             </resources>)",
+                          base_files_dir, &diag));
+  const std::string base_apk = GetTestPath("base.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest("com.aapt2.app"),
+      "-o", base_apk,
+  };
+  ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
+
+  const std::string feature_manifest = GetTestPath("feature_manifest.xml");
+  WriteFile(feature_manifest, android::base::StringPrintf(R"(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.aapt2.app" split="feature1">
+      </manifest>)"));
+  const std::string feature_files_dir = GetTestPath("feature");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+                          R"(<resources>
+                               <string name="foo">foo</string>
+                             </resources>)",
+                          feature_files_dir, &diag));
+  const std::string feature_apk = GetTestPath("feature.apk");
+  const std::string feature_package_id = "0x80";
+  link_args = {
+      "--manifest", feature_manifest,
+      "-I", base_apk,
+      "--package-id", feature_package_id,
+      "-o", feature_apk,
+  };
+  ASSERT_TRUE(Link(link_args, feature_files_dir, &diag));
+
+  const std::string feature2_manifest = GetTestPath("feature2_manifest.xml");
+  WriteFile(feature2_manifest, android::base::StringPrintf(R"(
+        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+            package="com.aapt2.app" split="feature2">
+          <uses-split android:name="feature1"/>
+        </manifest>)"));
+  const std::string feature2_files_dir = GetTestPath("feature2");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+                          R"(<resources>
+                               <string-array name="string_array">
+                                 <item>@string/bar</item>
+                                 <item>@string/foo</item>
+                               </string-array>
+                             </resources>)",
+                          feature2_files_dir, &diag));
+  const std::string feature2_apk = GetTestPath("feature2.apk");
+  const std::string feature2_package_id = "0x81";
+  link_args = {
+      "--manifest", feature2_manifest,
+      "-I", base_apk,
+      "-I", feature_apk,
+      "--package-id", feature2_package_id,
+      "-o", feature2_apk,
+  };
+  ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 2e6af18..5e06818 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -108,6 +108,12 @@
     return sdk_version_;
   }
 
+  const std::set<std::string>& GetSplitNameDependencies() override {
+    UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+    static std::set<std::string> empty;
+    return empty;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(OptimizeContext);