AAPT2: Enable mangled symbol lookup in AssetManagerSymbolTable

Bug: 70045583
Test: make AaptTestNamespace_Split
Test: make aapt2_tests
Change-Id: I25da9d58736fc9090d1527782391c9b2220d2f8d
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
index 94686c0..2da294c 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
@@ -20,6 +20,7 @@
 LOCAL_USE_AAPT2 := true
 LOCAL_AAPT_NAMESPACES := true
 LOCAL_PACKAGE_NAME := AaptTestNamespace_App
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_STATIC_ANDROID_LIBRARIES := \
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
index 1b80d95..174e746 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
@@ -26,4 +26,4 @@
             @com.android.aapt.namespace.libtwo:string/public_string
         </item>
     </style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
new file mode 100644
index 0000000..a35f6ed
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_PACKAGE_NAME := AaptTestNamespace_Split
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_APK_LIBRARIES := AaptTestNamespace_App
+LOCAL_RES_LIBRARIES := AaptTestNamespace_App
+LOCAL_AAPT_FLAGS := --package-id 0x80 --rename-manifest-package com.android.aapt.namespace.app
+include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml
new file mode 100644
index 0000000..bc9583b
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.aapt.namespace.split"
+    featureSplit="split">
+
+    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
+
+    <application>
+        <activity android:name=".SplitActivity" android:theme="@style/ActivityTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml
new file mode 100644
index 0000000..63da552
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="activity_name">Namespace Split</string>
+
+    <style name="ActivityTheme" parent="com.android.aapt.namespace.libone:style/Theme">
+        <item name="android:colorPrimary">#FF9800</item>
+        <item name="android:colorPrimaryDark">#E65100</item>
+        <item name="android:colorAccent">#FFD180</item>
+    </style>
+</resources>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java b/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java
new file mode 100644
index 0000000..3fff3cf
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.aapt.namespace.split;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SplitActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 2d517c7..0cfc0bd 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -30,7 +30,8 @@
 #include "ValueVisitor.h"
 #include "util/Util.h"
 
-using android::StringPiece;
+using ::android::StringPiece;
+using ::android::StringPiece16;
 
 namespace aapt {
 
@@ -291,11 +292,34 @@
   const std::u16string package16 = util::Utf8ToUtf16(name.package);
   const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type));
   const std::u16string entry16 = util::Utf8ToUtf16(name.entry);
+  const std::u16string mangled_entry16 =
+      util::Utf8ToUtf16(NameMangler::MangleEntry(name.package, name.entry));
 
-  uint32_t type_spec_flags = 0;
-  ResourceId res_id = table.identifierForName(
-      entry16.data(), entry16.size(), type16.data(), type16.size(),
-      package16.data(), package16.size(), &type_spec_flags);
+  uint32_t type_spec_flags;
+  ResourceId res_id;
+
+  // There can be mangled resources embedded within other packages. Here we will
+  // look into each package and look-up the mangled name until we find the resource.
+  const size_t count = table.getBasePackageCount();
+  for (size_t i = 0; i < count; i++) {
+    const android::String16 package_name = table.getBasePackageName(i);
+    StringPiece16 real_package16 = package16;
+    StringPiece16 real_entry16 = entry16;
+    std::u16string scratch_entry16;
+    if (StringPiece16(package_name) != package16) {
+      real_entry16 = mangled_entry16;
+      real_package16 = package_name.string();
+    }
+
+    type_spec_flags = 0;
+    res_id = table.identifierForName(real_entry16.data(), real_entry16.size(), type16.data(),
+                                     type16.size(), real_package16.data(), real_package16.size(),
+                                     &type_spec_flags);
+    if (res_id.is_valid()) {
+      break;
+    }
+  }
+
   if (!res_id.is_valid()) {
     return {};
   }
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index fd8a508..1f59d70 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -16,7 +16,15 @@
 
 #include "process/SymbolTable.h"
 
+#include "SdkConstants.h"
+#include "format/binary/TableFlattener.h"
 #include "test/Test.h"
+#include "util/BigBuffer.h"
+
+using ::testing::Eq;
+using ::testing::IsNull;
+using ::testing::Ne;
+using ::testing::NotNull;
 
 namespace aapt {
 
@@ -30,13 +38,13 @@
           .Build();
 
   ResourceTableSymbolSource symbol_source(table.get());
-  EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")));
-  EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")));
+  EXPECT_THAT(symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")), NotNull());
+  EXPECT_THAT(symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")), NotNull());
 
   std::unique_ptr<SymbolTable::Symbol> s =
       symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo"));
-  ASSERT_NE(nullptr, s);
-  EXPECT_NE(nullptr, s->attribute);
+  ASSERT_THAT(s, NotNull());
+  EXPECT_THAT(s->attribute, NotNull());
 }
 
 TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) {
@@ -49,8 +57,8 @@
   ResourceTableSymbolSource symbol_source(table.get());
   std::unique_ptr<SymbolTable::Symbol> s =
       symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo"));
-  ASSERT_NE(nullptr, s);
-  EXPECT_NE(nullptr, s->attribute);
+  ASSERT_THAT(s, NotNull());
+  EXPECT_THAT(s->attribute, NotNull());
 }
 
 TEST(SymbolTableTest, FindByName) {
@@ -64,8 +72,52 @@
   SymbolTable symbol_table(&mangler);
   symbol_table.AppendSource(util::make_unique<ResourceTableSymbolSource>(table.get()));
 
-  EXPECT_NE(nullptr, symbol_table.FindByName(test::ParseNameOrDie("id/foo")));
-  EXPECT_NE(nullptr, symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")));
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("id/foo")), NotNull());
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
+}
+
+TEST(SymbolTableTest, FindByNameWhenSymbolIsMangledInResTable) {
+  using namespace android;
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .SetCompilationPackage("com.android.app")
+          .SetPackageId(0x7f)
+          .SetPackageType(PackageType::kApp)
+          .SetMinSdkVersion(SDK_LOLLIPOP_MR1)
+          .SetNameManglerPolicy(NameManglerPolicy{"com.android.app"})
+          .Build();
+
+  // Create a ResourceTable with a mangled resource, simulating a static library being merged into
+  // the main application package.
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddSimple("com.android.app:id/" + NameMangler::MangleEntry("com.android.lib", "foo"),
+                     ResourceId(0x7f020000))
+          .AddSimple("com.android.app:id/bar", ResourceId(0x7f020001))
+          .Build();
+
+  BigBuffer buffer(1024u);
+  TableFlattener flattener({}, &buffer);
+  ASSERT_TRUE(flattener.Consume(context.get(), table.get()));
+
+  std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+
+  // Construct the test AssetManager.
+  auto asset_manager_source = util::make_unique<AssetManagerSymbolSource>();
+  ResTable& res_table = const_cast<ResTable&>(
+      asset_manager_source->GetAssetManager()->getResources(false /*required*/));
+  ASSERT_THAT(res_table.add(data.get(), buffer.size()), Eq(NO_ERROR));
+
+  SymbolTable symbol_table(context->GetNameMangler());
+  symbol_table.AppendSource(std::move(asset_manager_source));
+
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.app:id/bar")), NotNull());
+
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.app:id/foo")), IsNull());
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/bar")), IsNull());
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.other:id/foo")), IsNull());
 }
 
 }  // namespace aapt