Files
frameworks_base/tools/aapt2/cmd/Link_test.cpp
Ryan Mitchell 5855de7ed6 Use R field directly for shared lib styleable attrs
If a shared library exposes an attribute and a client uses the
attribute in its own styleable, the value of the lib attribute
resource id in the client styleable must be fixed with the correct
package id at runtime. Since the client will not have an
onResourcesLoaded to call, the client should directly reference the
attribute resource field in its styleable.

Bug: 147674078
Test: aapt2_tests
Change-Id: I8e64bb2d3165a7072e2604fe1730b248545978f4
2021-02-24 15:57:34 -08:00

396 lines
15 KiB
C++

/*
* 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 "Link.h"
#include <android-base/file.h>
#include "AppInfo.h"
#include "LoadedApk.h"
#include "test/Test.h"
using testing::Eq;
using testing::HasSubstr;
using testing::Ne;
namespace aapt {
using LinkTest = CommandTestFixture;
TEST_F(LinkTest, RemoveRawXmlStrings) {
StdErrDiagnostics diag;
const std::string compiled_files_dir = GetTestPath("compiled");
ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
compiled_files_dir, &diag));
const std::string out_apk = GetTestPath("out.apk");
std::vector<std::string> link_args = {
"--manifest", GetDefaultManifest(),
"-o", out_apk,
};
ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
ASSERT_THAT(data, Ne(nullptr));
AssertLoadXml(apk.get(), data.get(), &tree);
// Check that the raw string index has not been assigned
EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
}
TEST_F(LinkTest, KeepRawXmlStrings) {
StdErrDiagnostics diag;
const std::string compiled_files_dir = GetTestPath("compiled");
ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
compiled_files_dir, &diag));
const std::string out_apk = GetTestPath("out.apk");
std::vector<std::string> link_args = {
"--manifest", GetDefaultManifest(),
"-o", out_apk,
"--keep-raw-values"
};
ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
ASSERT_THAT(data, Ne(nullptr));
AssertLoadXml(apk.get(), data.get(), &tree);
// Check that the raw string index has been set to the correct string pool entry
int32_t raw_index = tree.getAttributeValueStringID(0);
ASSERT_THAT(raw_index, Ne(-1));
EXPECT_THAT(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007"));
}
TEST_F(LinkTest, NoCompressAssets) {
StdErrDiagnostics diag;
std::string content(500, 'a');
WriteFile(GetTestPath("assets/testtxt"), content);
WriteFile(GetTestPath("assets/testtxt2"), content);
WriteFile(GetTestPath("assets/test.txt"), content);
WriteFile(GetTestPath("assets/test.hello.txt"), content);
WriteFile(GetTestPath("assets/test.hello.xml"), content);
const std::string out_apk = GetTestPath("out.apk");
std::vector<std::string> link_args = {
"--manifest", GetDefaultManifest(),
"-o", out_apk,
"-0", ".txt",
"-0", "txt2",
"-0", ".hello.txt",
"-0", "hello.xml",
"-A", GetTestPath("assets")
};
ASSERT_TRUE(Link(link_args, &diag));
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
ASSERT_THAT(apk, Ne(nullptr));
io::IFileCollection* zip = apk->GetFileCollection();
ASSERT_THAT(zip, Ne(nullptr));
auto file = zip->FindFile("assets/testtxt");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_TRUE(file->WasCompressed());
file = zip->FindFile("assets/testtxt2");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_FALSE(file->WasCompressed());
file = zip->FindFile("assets/test.txt");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_FALSE(file->WasCompressed());
file = zip->FindFile("assets/test.hello.txt");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_FALSE(file->WasCompressed());
file = zip->FindFile("assets/test.hello.xml");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_FALSE(file->WasCompressed());
}
TEST_F(LinkTest, NoCompressResources) {
StdErrDiagnostics diag;
std::string content(500, 'a');
const std::string compiled_files_dir = GetTestPath("compiled");
ASSERT_TRUE(CompileFile(GetTestPath("res/raw/testtxt"), content, compiled_files_dir, &diag));
ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test.txt"), content, compiled_files_dir, &diag));
ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test1.hello.txt"), content, compiled_files_dir,
&diag));
ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test2.goodbye.xml"), content, compiled_files_dir,
&diag));
const std::string out_apk = GetTestPath("out.apk");
std::vector<std::string> link_args = {
"--manifest", GetDefaultManifest(),
"-o", out_apk,
"-0", ".txt",
"-0", ".hello.txt",
"-0", "goodbye.xml",
};
ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
ASSERT_THAT(apk, Ne(nullptr));
io::IFileCollection* zip = apk->GetFileCollection();
ASSERT_THAT(zip, Ne(nullptr));
auto file = zip->FindFile("res/raw/testtxt");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_TRUE(file->WasCompressed());
file = zip->FindFile("res/raw/test.txt");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_FALSE(file->WasCompressed());
file = zip->FindFile("res/raw/test1.hello.hello.txt");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_FALSE(file->WasCompressed());
file = zip->FindFile("res/raw/test2.goodbye.goodbye.xml");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_FALSE(file->WasCompressed());
}
TEST_F(LinkTest, OverlayStyles) {
StdErrDiagnostics diag;
const std::string compiled_files_dir = GetTestPath("compiled");
const std::string override_files_dir = GetTestPath("compiled-override");
ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
R"(<resources>
<style name="MyStyle">
<item name="android:textColor">#123</item>
</style>
</resources>)",
compiled_files_dir, &diag));
ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
R"(<resources>
<style name="MyStyle">
<item name="android:background">#456</item>
</style>
</resources>)",
override_files_dir, &diag));
const std::string out_apk = GetTestPath("out.apk");
std::vector<std::string> link_args = {
"--manifest", GetDefaultManifest(kDefaultPackageName),
"-o", out_apk,
};
const auto override_files = file::FindFiles(override_files_dir, &diag);
for (const auto &override_file : override_files.value()) {
link_args.push_back("-R");
link_args.push_back(file::BuildPath({override_files_dir, override_file}));
}
ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
const Style* actual_style = test::GetValue<Style>(
apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
ASSERT_NE(actual_style, nullptr);
ASSERT_EQ(actual_style->entries.size(), 2);
EXPECT_EQ(actual_style->entries[0].key.id, 0x01010098); // android:textColor
EXPECT_EQ(actual_style->entries[1].key.id, 0x010100d4); // android:background
}
TEST_F(LinkTest, OverrideStylesInsteadOfOverlaying) {
StdErrDiagnostics diag;
const std::string compiled_files_dir = GetTestPath("compiled");
const std::string override_files_dir = GetTestPath("compiled-override");
ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
R"(<resources>
<style name="MyStyle">
<item name="android:textColor">#123</item>
</style>
</resources>)",
compiled_files_dir, &diag));
ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
R"(<resources>
<style name="MyStyle">
<item name="android:background">#456</item>
</style>
</resources>)",
override_files_dir, &diag));
const std::string out_apk = GetTestPath("out.apk");
std::vector<std::string> link_args = {
"--manifest", GetDefaultManifest(kDefaultPackageName),
"--override-styles-instead-of-overlaying",
"-o", out_apk,
};
const auto override_files = file::FindFiles(override_files_dir, &diag);
for (const auto &override_file : override_files.value()) {
link_args.push_back("-R");
link_args.push_back(file::BuildPath({override_files_dir, override_file}));
}
ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
const Style* actual_style = test::GetValue<Style>(
apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
ASSERT_NE(actual_style, nullptr);
ASSERT_EQ(actual_style->entries.size(), 1);
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));
}
TEST_F(LinkTest, SharedLibraryAttributeRJava) {
StdErrDiagnostics diag;
const std::string lib_values =
R"(<resources>
<attr name="foo"/>
<public type="attr" name="foo" id="0x00010001"/>
<declare-styleable name="LibraryStyleable">
<attr name="foo" />
</declare-styleable>
</resources>)";
const std::string client_values =
R"(<resources>
<attr name="bar" />
<declare-styleable name="ClientStyleable">
<attr name="com.example.lib:foo" />
<attr name="bar" />
</declare-styleable>
</resources>)";
// Build a library with a public attribute
const std::string lib_res = GetTestPath("library-res");
ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), lib_values, lib_res, &diag));
const std::string lib_apk = GetTestPath("library.apk");
const std::string lib_java = GetTestPath("library_java");
// clang-format off
auto lib_manifest = ManifestBuilder(this)
.SetPackageName("com.example.lib")
.Build();
auto lib_link_args = LinkCommandBuilder(this)
.SetManifestFile(lib_manifest)
.AddFlag("--shared-lib")
.AddParameter("--java", lib_java)
.AddCompiledResDir(lib_res, &diag)
.Build(lib_apk);
// clang-format on
ASSERT_TRUE(Link(lib_link_args, &diag));
const std::string lib_r_java = lib_java + "/com/example/lib/R.java";
std::string lib_r_contents;
ASSERT_TRUE(android::base::ReadFileToString(lib_r_java, &lib_r_contents));
EXPECT_THAT(lib_r_contents, HasSubstr(" public static int foo=0x00010001;"));
EXPECT_THAT(lib_r_contents, HasSubstr(" com.example.lib.R.attr.foo"));
// Build a client that uses the library attribute in a declare-styleable
const std::string client_res = GetTestPath("client-res");
ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), client_values, client_res, &diag));
const std::string client_apk = GetTestPath("client.apk");
const std::string client_java = GetTestPath("client_java");
// clang-format off
auto client_manifest = ManifestBuilder(this)
.SetPackageName("com.example.client")
.Build();
auto client_link_args = LinkCommandBuilder(this)
.SetManifestFile(client_manifest)
.AddParameter("--java", client_java)
.AddParameter("-I", lib_apk)
.AddCompiledResDir(client_res, &diag)
.Build(client_apk);
// clang-format on
ASSERT_TRUE(Link(client_link_args, &diag));
const std::string client_r_java = client_java + "/com/example/client/R.java";
std::string client_r_contents;
ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
}
} // namespace aapt