summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/Command.cpp11
-rw-r--r--tools/aapt/ResourceTable.cpp4
-rw-r--r--tools/aapt/SdkConstants.h1
-rw-r--r--tools/aapt2/Android.bp17
-rw-r--r--tools/aapt2/ConfigDescription.cpp6
-rw-r--r--tools/aapt2/ConfigDescription.h3
-rw-r--r--tools/aapt2/Configuration.proto207
-rw-r--r--tools/aapt2/Debug.cpp12
-rw-r--r--tools/aapt2/LoadedApk.cpp58
-rw-r--r--tools/aapt2/LoadedApk.h48
-rw-r--r--tools/aapt2/Locale.cpp137
-rw-r--r--tools/aapt2/Locale.h5
-rw-r--r--tools/aapt2/NameMangler.h7
-rw-r--r--tools/aapt2/ResourceParser.cpp128
-rw-r--r--tools/aapt2/ResourceParser.h1
-rw-r--r--tools/aapt2/ResourceParser_test.cpp45
-rw-r--r--tools/aapt2/ResourceTable.cpp30
-rw-r--r--tools/aapt2/ResourceTable.h2
-rw-r--r--tools/aapt2/ResourceUtils.cpp6
-rw-r--r--tools/aapt2/ResourceValues.cpp14
-rw-r--r--tools/aapt2/ResourceValues.h62
-rw-r--r--tools/aapt2/Resources.proto241
-rw-r--r--tools/aapt2/ResourcesInternal.proto21
-rw-r--r--tools/aapt2/SdkConstants.cpp679
-rw-r--r--tools/aapt2/SdkConstants.h2
-rw-r--r--tools/aapt2/ValueVisitor.h116
-rw-r--r--tools/aapt2/ValueVisitor_test.cpp10
-rw-r--r--tools/aapt2/cmd/Compile.cpp33
-rw-r--r--tools/aapt2/cmd/Diff.cpp6
-rw-r--r--tools/aapt2/cmd/Dump.cpp144
-rw-r--r--tools/aapt2/cmd/Link.cpp226
-rw-r--r--tools/aapt2/cmd/Optimize.cpp136
-rw-r--r--tools/aapt2/compile/InlineXmlFormatParser.cpp6
-rw-r--r--tools/aapt2/compile/PngChunkFilter.cpp4
-rw-r--r--tools/aapt2/compile/PseudolocaleGenerator.cpp6
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.cpp183
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.h48
-rw-r--r--tools/aapt2/configuration/ConfigurationParser_test.cpp363
-rw-r--r--tools/aapt2/configuration/aapt2.xsd1
-rw-r--r--tools/aapt2/filter/ConfigFilter.h10
-rw-r--r--tools/aapt2/filter/Filter_test.cpp8
-rw-r--r--tools/aapt2/format/Archive.cpp (renamed from tools/aapt2/flatten/Archive.cpp)18
-rw-r--r--tools/aapt2/format/Archive.h (renamed from tools/aapt2/flatten/Archive.h)6
-rw-r--r--tools/aapt2/format/binary/BinaryResourceParser.cpp (renamed from tools/aapt2/unflatten/BinaryResourceParser.cpp)170
-rw-r--r--tools/aapt2/format/binary/BinaryResourceParser.h (renamed from tools/aapt2/unflatten/BinaryResourceParser.h)51
-rw-r--r--tools/aapt2/format/binary/ChunkWriter.h (renamed from tools/aapt2/flatten/ChunkWriter.h)21
-rw-r--r--tools/aapt2/format/binary/ResChunkPullParser.cpp (renamed from tools/aapt2/unflatten/ResChunkPullParser.cpp)7
-rw-r--r--tools/aapt2/format/binary/ResChunkPullParser.h (renamed from tools/aapt2/unflatten/ResChunkPullParser.h)55
-rw-r--r--tools/aapt2/format/binary/ResourceTypeExtensions.h (renamed from tools/aapt2/flatten/ResourceTypeExtensions.h)12
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp (renamed from tools/aapt2/flatten/TableFlattener.cpp)50
-rw-r--r--tools/aapt2/format/binary/TableFlattener.h (renamed from tools/aapt2/flatten/TableFlattener.h)9
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp (renamed from tools/aapt2/flatten/TableFlattener_test.cpp)112
-rw-r--r--tools/aapt2/format/binary/XmlFlattener.cpp (renamed from tools/aapt2/flatten/XmlFlattener.cpp)32
-rw-r--r--tools/aapt2/format/binary/XmlFlattener.h (renamed from tools/aapt2/flatten/XmlFlattener.h)17
-rw-r--r--tools/aapt2/format/binary/XmlFlattener_test.cpp (renamed from tools/aapt2/flatten/XmlFlattener_test.cpp)5
-rw-r--r--tools/aapt2/format/proto/ProtoDeserialize.cpp918
-rw-r--r--tools/aapt2/format/proto/ProtoDeserialize.h78
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.cpp622
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.h89
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize_test.cpp435
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/Android.mk2
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/Android.mk29
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/AndroidManifest.xml30
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/res/layout/activity_main.xml29
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml29
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/src/com/android/aapt/namespace/app/MainActivity.java34
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk28
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibOne/AndroidManifest.xml21
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibOne/res/values/values.xml28
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk29
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibTwo/AndroidManifest.xml21
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibTwo/res/values/values.xml27
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibTwo/src/com/android/aapt/namespace/libtwo/TextView.java53
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/Android.mk2
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/Android.mk (renamed from tools/aapt2/integration-tests/AppOne/Android.mk)6
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml (renamed from tools/aapt2/integration-tests/AppOne/AndroidManifest.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/assets/subdir/subsubdir/test.txt (renamed from tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/assets/test.txt (renamed from tools/aapt2/integration-tests/AppOne/assets/test.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/assets2/new.txt (renamed from tools/aapt2/integration-tests/AppOne/assets2/new.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/assets2/test.txt (renamed from tools/aapt2/integration-tests/AppOne/assets2/test.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/cheap_transparency.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png)bin397 -> 397 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/complex.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png)bin409 -> 409 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/icon.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/icon.png)bin2341 -> 2341 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/image.xml (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/image.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/outline_8x8.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png)bin191 -> 191 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_off_center_outline_32x16.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png)bin196 -> 196 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_outline_32x16.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png)bin181 -> 181 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/test.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png)bin124 -> 124 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_3x3.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png)bin105 -> 105 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_optical_bounds_3x3.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png)bin103 -> 103 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_3x3.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png)bin103 -> 103 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_optical_bounds_3x3.9.png (renamed from tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png)bin105 -> 105 bytes
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-italic.ttf (renamed from tools/aapt2/integration-tests/AppOne/res/font/myfont-italic.ttf)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-normal.ttf (renamed from tools/aapt2/integration-tests/AppOne/res/font/myfont-normal.ttf)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont.xml (renamed from tools/aapt2/integration-tests/AppOne/res/font/myfont.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/layout-v21/main.xml (renamed from tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml)1
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml (renamed from tools/aapt2/integration-tests/AppOne/res/layout/main.xml)1
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/layout/special.xml (renamed from tools/aapt2/integration-tests/AppOne/res/layout/special.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/navigation/home.xml (renamed from tools/aapt2/integration-tests/AppOne/res/navigation/home.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/raw/test.txt (renamed from tools/aapt2/integration-tests/AppOne/res/raw/test.txt)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/transition/transition_set.xml (renamed from tools/aapt2/integration-tests/AppOne/res/transition/transition_set.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/values-v4/styles.xml (renamed from tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/values/colors.xml (renamed from tools/aapt2/integration-tests/AppOne/res/values/colors.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/values/styles.xml (renamed from tools/aapt2/integration-tests/AppOne/res/values/styles.xml)2
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/res/values/test.xml (renamed from tools/aapt2/integration-tests/AppOne/res/values/test.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/src/com/android/aapt/app/one/AppOne.java (renamed from tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk (renamed from tools/aapt2/integration-tests/StaticLibOne/Android.mk)4
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/AndroidManifest.xml (renamed from tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/res/layout/layout.xml (renamed from tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/res/values/values.xml (renamed from tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java (renamed from tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk (renamed from tools/aapt2/integration-tests/StaticLibTwo/Android.mk)4
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/AndroidManifest.xml (renamed from tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/drawable/vector.xml (renamed from tools/aapt2/integration-tests/StaticLibTwo/res/drawable/vector.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/layout/layout_two.xml (renamed from tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/values/values.xml (renamed from tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml)0
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java (renamed from tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java)0
-rw-r--r--tools/aapt2/io/Util.h2
-rw-r--r--tools/aapt2/java/AnnotationProcessor.cpp65
-rw-r--r--tools/aapt2/java/AnnotationProcessor.h75
-rw-r--r--tools/aapt2/java/AnnotationProcessor_test.cpp17
-rw-r--r--tools/aapt2/java/ClassDefinition.cpp9
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp5
-rw-r--r--tools/aapt2/java/JavaClassGenerator_test.cpp144
-rw-r--r--tools/aapt2/java/ManifestClassGenerator.cpp57
-rw-r--r--tools/aapt2/java/ManifestClassGenerator_test.cpp9
-rw-r--r--tools/aapt2/link/Linkers.h88
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp218
-rw-r--r--tools/aapt2/link/ReferenceLinker.h88
-rw-r--r--tools/aapt2/link/ReferenceLinker_test.cpp31
-rw-r--r--tools/aapt2/link/TableMerger.cpp96
-rw-r--r--tools/aapt2/link/TableMerger.h86
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp61
-rw-r--r--tools/aapt2/optimize/MultiApkGenerator.cpp355
-rw-r--r--tools/aapt2/optimize/MultiApkGenerator.h67
-rw-r--r--tools/aapt2/optimize/MultiApkGenerator_test.cpp222
-rw-r--r--tools/aapt2/optimize/VersionCollapser.cpp28
-rw-r--r--tools/aapt2/proto/ProtoHelpers.cpp161
-rw-r--r--tools/aapt2/proto/ProtoHelpers.h57
-rw-r--r--tools/aapt2/proto/ProtoSerialize.h75
-rw-r--r--tools/aapt2/proto/TableProtoDeserializer.cpp423
-rw-r--r--tools/aapt2/proto/TableProtoSerializer.cpp399
-rw-r--r--tools/aapt2/proto/TableProtoSerializer_test.cpp242
-rw-r--r--tools/aapt2/split/TableSplitter.cpp70
-rw-r--r--tools/aapt2/test/Builders.cpp72
-rw-r--r--tools/aapt2/test/Builders.h36
-rw-r--r--tools/aapt2/test/Common.h4
-rw-r--r--tools/aapt2/util/Util.cpp5
-rw-r--r--tools/aapt2/util/Util.h4
-rw-r--r--tools/aapt2/xml/XmlDom.cpp33
-rw-r--r--tools/aapt2/xml/XmlDom.h60
-rw-r--r--tools/aapt2/xml/XmlDom_test.cpp20
-rw-r--r--tools/aapt2/xml/XmlPullParser.cpp7
-rw-r--r--tools/aapt2/xml/XmlPullParser.h3
-rw-r--r--tools/aapt2/xml/XmlUtil.cpp10
-rw-r--r--tools/aapt2/xml/XmlUtil.h15
-rw-r--r--tools/apilint/apilint.py76
-rw-r--r--tools/bit/make.cpp2
-rw-r--r--tools/incident_report/generic_message.h2
-rw-r--r--tools/incident_report/main.cpp66
-rw-r--r--tools/incident_section_gen/main.cpp206
-rw-r--r--tools/streaming_proto/Android.bp24
-rw-r--r--tools/streaming_proto/Errors.cpp4
-rw-r--r--tools/streaming_proto/Errors.h4
-rw-r--r--tools/streaming_proto/cpp/main.cpp273
-rw-r--r--tools/streaming_proto/java/main.cpp (renamed from tools/streaming_proto/main.cpp)2
-rw-r--r--tools/streaming_proto/string_utils.cpp23
-rw-r--r--tools/streaming_proto/string_utils.h9
-rw-r--r--tools/validatekeymaps/Main.cpp55
169 files changed, 6469 insertions, 3830 deletions
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 5e8580255197..cb87737c6868 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -739,12 +739,8 @@ int doDump(Bundle* bundle)
AssetManager assets;
int32_t assetsCookie;
- if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
- fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
- return 1;
- }
- // Now add any dependencies passed in.
+ // Add any dependencies passed in.
for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
const String8& assetPath = bundle->getPackageIncludes()[i];
if (!assets.addAssetPath(assetPath, NULL)) {
@@ -753,6 +749,11 @@ int doDump(Bundle* bundle)
}
}
+ if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
+ fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
+ return 1;
+ }
+
// Make a dummy config for retrieving resources... we need to supply
// non-default values for some configs so that we can retrieve resources
// in the app that don't have a default. The most important of these is
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 52b93a945433..669afe18af88 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -4847,6 +4847,7 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle,
const String16 animatedVector16("animated-vector");
const String16 pathInterpolator16("pathInterpolator");
const String16 objectAnimator16("objectAnimator");
+ const String16 gradient16("gradient");
const int minSdk = getMinSdkVersion(bundle);
if (minSdk >= SDK_LOLLIPOP_MR1) {
@@ -4874,7 +4875,8 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle,
if (bundle->getNoVersionVectors() && (node->getElementName() == vector16 ||
node->getElementName() == animatedVector16 ||
node->getElementName() == objectAnimator16 ||
- node->getElementName() == pathInterpolator16)) {
+ node->getElementName() == pathInterpolator16 ||
+ node->getElementName() == gradient16)) {
// We were told not to version vector tags, so skip the children here.
continue;
}
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index bf56ec024699..b982d0d2ea63 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -43,6 +43,7 @@ enum {
SDK_NOUGAT_MR1 = 25,
SDK_O = 26,
SDK_O_MR1 = 27,
+ SDK_P = 10000, // STOPSHIP Replace with the real version.
};
#endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index c6cfd3032762..ff51d5146eb8 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -83,9 +83,13 @@ cc_library_host_static {
"configuration/ConfigurationParser.cpp",
"filter/AbiFilter.cpp",
"filter/ConfigFilter.cpp",
- "flatten/Archive.cpp",
- "flatten/TableFlattener.cpp",
- "flatten/XmlFlattener.cpp",
+ "format/Archive.cpp",
+ "format/binary/BinaryResourceParser.cpp",
+ "format/binary/ResChunkPullParser.cpp",
+ "format/binary/TableFlattener.cpp",
+ "format/binary/XmlFlattener.cpp",
+ "format/proto/ProtoDeserialize.cpp",
+ "format/proto/ProtoSerialize.cpp",
"io/BigBufferStreams.cpp",
"io/File.cpp",
"io/FileInputStream.cpp",
@@ -102,17 +106,13 @@ cc_library_host_static {
"link/XmlCompatVersioner.cpp",
"link/XmlNamespaceRemover.cpp",
"link/XmlReferenceLinker.cpp",
+ "optimize/MultiApkGenerator.cpp",
"optimize/ResourceDeduper.cpp",
"optimize/VersionCollapser.cpp",
"process/SymbolTable.cpp",
- "proto/ProtoHelpers.cpp",
- "proto/TableProtoDeserializer.cpp",
- "proto/TableProtoSerializer.cpp",
"split/TableSplitter.cpp",
"text/Unicode.cpp",
"text/Utf8Iterator.cpp",
- "unflatten/BinaryResourceParser.cpp",
- "unflatten/ResChunkPullParser.cpp",
"util/BigBuffer.cpp",
"util/Files.cpp",
"util/Util.cpp",
@@ -138,6 +138,7 @@ cc_library_host_static {
"xml/XmlDom.cpp",
"xml/XmlPullParser.cpp",
"xml/XmlUtil.cpp",
+ "Configuration.proto",
"Resources.proto",
"ResourcesInternal.proto",
],
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index a9278c136cff..59a6e1281783 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -876,6 +876,12 @@ ConfigDescription ConfigDescription::CopyWithoutSdkVersion() const {
return copy;
}
+std::string ConfigDescription::GetBcp47LanguageTag(bool canonicalize) const {
+ char locale[RESTABLE_MAX_LOCALE_LEN];
+ getBcp47Locale(locale, canonicalize);
+ return std::string(locale);
+}
+
bool ConfigDescription::Dominates(const ConfigDescription& o) const {
if (*this == o) {
return true;
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 65c96175091c..c1d0e1084186 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -61,6 +61,9 @@ struct ConfigDescription : public android::ResTable_config {
ConfigDescription CopyWithoutSdkVersion() const;
+ // Returns the BCP-47 language tag of this configuration's locale.
+ std::string GetBcp47LanguageTag(bool canonicalize = false) const;
+
/**
* A configuration X dominates another configuration Y, if X has at least the
* precedence of Y and X is strictly more general than Y: for any type defined
diff --git a/tools/aapt2/Configuration.proto b/tools/aapt2/Configuration.proto
new file mode 100644
index 000000000000..fc636a43ec40
--- /dev/null
+++ b/tools/aapt2/Configuration.proto
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package aapt.pb;
+
+option java_package = "com.android.aapt";
+option optimize_for = LITE_RUNTIME;
+
+// A description of the requirements a device must have in order for a
+// resource to be matched and selected.
+message Configuration {
+ enum LayoutDirection {
+ LAYOUT_DIRECTION_UNSET = 0;
+ LAYOUT_DIRECTION_LTR = 1;
+ LAYOUT_DIRECTION_RTL = 2;
+ }
+
+ enum ScreenLayoutSize {
+ SCREEN_LAYOUT_SIZE_UNSET = 0;
+ SCREEN_LAYOUT_SIZE_SMALL = 1;
+ SCREEN_LAYOUT_SIZE_NORMAL = 2;
+ SCREEN_LAYOUT_SIZE_LARGE = 3;
+ SCREEN_LAYOUT_SIZE_XLARGE = 4;
+ }
+
+ enum ScreenLayoutLong {
+ SCREEN_LAYOUT_LONG_UNSET = 0;
+ SCREEN_LAYOUT_LONG_LONG = 1;
+ SCREEN_LAYOUT_LONG_NOTLONG = 2;
+ }
+
+ enum ScreenRound {
+ SCREEN_ROUND_UNSET = 0;
+ SCREEN_ROUND_ROUND = 1;
+ SCREEN_ROUND_NOTROUND = 2;
+ }
+
+ enum WideColorGamut {
+ WIDE_COLOR_GAMUT_UNSET = 0;
+ WIDE_COLOR_GAMUT_WIDECG = 1;
+ WIDE_COLOR_GAMUT_NOWIDECG = 2;
+ }
+
+ enum Hdr {
+ HDR_UNSET = 0;
+ HDR_HIGHDR = 1;
+ HDR_LOWDR = 2;
+ }
+
+ enum Orientation {
+ ORIENTATION_UNSET = 0;
+ ORIENTATION_PORT = 1;
+ ORIENTATION_LAND = 2;
+ ORIENTATION_SQUARE = 3;
+ }
+
+ enum UiModeType {
+ UI_MODE_TYPE_UNSET = 0;
+ UI_MODE_TYPE_NORMAL = 1;
+ UI_MODE_TYPE_DESK = 2;
+ UI_MODE_TYPE_CAR = 3;
+ UI_MODE_TYPE_TELEVISION = 4;
+ UI_MODE_TYPE_APPLIANCE = 5;
+ UI_MODE_TYPE_WATCH = 6;
+ UI_MODE_TYPE_VRHEADSET = 7;
+ }
+
+ enum UiModeNight {
+ UI_MODE_NIGHT_UNSET = 0;
+ UI_MODE_NIGHT_NIGHT = 1;
+ UI_MODE_NIGHT_NOTNIGHT = 2;
+ }
+
+ enum Touchscreen {
+ TOUCHSCREEN_UNSET = 0;
+ TOUCHSCREEN_NOTOUCH = 1;
+ TOUCHSCREEN_STYLUS = 2;
+ TOUCHSCREEN_FINGER = 3;
+ }
+
+ enum KeysHidden {
+ KEYS_HIDDEN_UNSET = 0;
+ KEYS_HIDDEN_KEYSEXPOSED = 1;
+ KEYS_HIDDEN_KEYSHIDDEN = 2;
+ KEYS_HIDDEN_KEYSSOFT = 3;
+ }
+
+ enum Keyboard {
+ KEYBOARD_UNSET = 0;
+ KEYBOARD_NOKEYS = 1;
+ KEYBOARD_QWERTY = 2;
+ KEYBOARD_TWELVEKEY = 3;
+ }
+
+ enum NavHidden {
+ NAV_HIDDEN_UNSET = 0;
+ NAV_HIDDEN_NAVEXPOSED = 1;
+ NAV_HIDDEN_NAVHIDDEN = 2;
+ }
+
+ enum Navigation {
+ NAVIGATION_UNSET = 0;
+ NAVIGATION_NONAV = 1;
+ NAVIGATION_DPAD = 2;
+ NAVIGATION_TRACKBALL = 3;
+ NAVIGATION_WHEEL = 4;
+ }
+
+ //
+ // Axis/dimensions that are understood by the runtime.
+ //
+
+ // Mobile country code.
+ uint32 mcc = 1;
+
+ // Mobile network code.
+ uint32 mnc = 2;
+
+ // BCP-47 locale tag.
+ string locale = 3;
+
+ // Left-to-right, right-to-left...
+ LayoutDirection layout_direction = 4;
+
+ // Screen width in pixels. Prefer screen_width_dp.
+ uint32 screen_width = 5;
+
+ // Screen height in pixels. Prefer screen_height_dp.
+ uint32 screen_height = 6;
+
+ // Screen width in density independent pixels (dp).
+ uint32 screen_width_dp = 7;
+
+ // Screen height in density independent pixels (dp).
+ uint32 screen_height_dp = 8;
+
+ // The smallest screen dimension, regardless of orientation, in dp.
+ uint32 smallest_screen_width_dp = 9;
+
+ // Whether the device screen is classified as small, normal, large, xlarge.
+ ScreenLayoutSize screen_layout_size = 10;
+
+ // Whether the device screen is long.
+ ScreenLayoutLong screen_layout_long = 11;
+
+ // Whether the screen is round (Android Wear).
+ ScreenRound screen_round = 12;
+
+ // Whether the screen supports wide color gamut.
+ WideColorGamut wide_color_gamut = 13;
+
+ // Whether the screen has high dynamic range.
+ Hdr hdr = 14;
+
+ // Which orientation the device is in (portrait, landscape).
+ Orientation orientation = 15;
+
+ // Which type of UI mode the device is in (television, car, etc.).
+ UiModeType ui_mode_type = 16;
+
+ // Whether the device is in night mode.
+ UiModeNight ui_mode_night = 17;
+
+ // The device's screen density in dots-per-inch (dpi).
+ uint32 density = 18;
+
+ // Whether a touchscreen exists, supports a stylus, or finger.
+ Touchscreen touchscreen = 19;
+
+ // Whether the keyboard hardware keys are currently hidden, exposed, or
+ // if the keyboard is a software keyboard.
+ KeysHidden keys_hidden = 20;
+
+ // The type of keyboard present (none, QWERTY, 12-key).
+ Keyboard keyboard = 21;
+
+ // Whether the navigation is exposed or hidden.
+ NavHidden nav_hidden = 22;
+
+ // The type of navigation present on the device
+ // (trackball, wheel, dpad, etc.).
+ Navigation navigation = 23;
+
+ // The minimum SDK version of the device.
+ uint32 sdk_version = 24;
+
+ //
+ // Build-time only dimensions.
+ //
+
+ string product = 25;
+}
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 49ed7780f950..1555d6126493 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -35,9 +35,9 @@ namespace aapt {
namespace {
-class PrintVisitor : public ValueVisitor {
+class PrintVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
void Visit(Attribute* attr) override {
std::cout << "(attr) type=";
@@ -291,7 +291,13 @@ class XmlPrinter : public xml::Visitor {
std::cerr << "(" << attr.compiled_attribute.value().id.value_or_default(ResourceId(0x0))
<< ")";
}
- std::cerr << "=" << attr.value << "\n";
+ std::cerr << "=";
+ if (attr.compiled_value != nullptr) {
+ std::cerr << *attr.compiled_value;
+ } else {
+ std::cerr << attr.value;
+ }
+ std::cerr << "\n";
}
prefix_ += " ";
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 7e5efa15f61b..c1815c82b5b5 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -18,13 +18,17 @@
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
#include "io/BigBufferInputStream.h"
#include "io/Util.h"
+#include "xml/XmlDom.h"
namespace aapt {
+using xml::XmlResource;
+
std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
const android::StringPiece& path) {
Source source(path);
@@ -52,20 +56,22 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
if (!parser.Parse()) {
return {};
}
+
return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
}
bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
IArchiveWriter* writer) {
FilterChain empty;
- return WriteToArchive(context, options, &empty, writer);
+ return WriteToArchive(context, table_.get(), options, &empty, writer);
}
-bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
- FilterChain* filters, IArchiveWriter* writer) {
+bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table,
+ const TableFlattenerOptions& options, FilterChain* filters,
+ IArchiveWriter* writer, XmlResource* manifest) {
std::set<std::string> referenced_resources;
// List the files being referenced in the resource table.
- for (auto& pkg : table_->packages) {
+ for (auto& pkg : split_table->packages) {
for (auto& type : pkg->types) {
for (auto& entry : type->entries) {
for (auto& config_value : entry->values) {
@@ -84,7 +90,7 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
std::string path = file->GetSource().path;
// The name of the path has the format "<zip-file-name>@<path-to-file>".
- path = path.substr(path.find("@") + 1);
+ path = path.substr(path.find('@') + 1);
// Skip resources that are not referenced if requested.
if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
@@ -108,7 +114,7 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
// TODO(adamlesinski): How to determine if there were sparse entries (and if to encode
// with sparse entries) b/35389232.
TableFlattener flattener(options, &buffer);
- if (!flattener.Consume(context, table_.get())) {
+ if (!flattener.Consume(context, split_table)) {
return false;
}
@@ -118,6 +124,20 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
return false;
}
+ } else if (manifest != nullptr && path == "AndroidManifest.xml") {
+ BigBuffer buffer(8192);
+ XmlFlattener xml_flattener(&buffer, {});
+ if (!xml_flattener.Consume(context, manifest)) {
+ context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed");
+ return false;
+ }
+
+ uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
+ io::BigBufferInputStream manifest_buffer_in(&buffer);
+ if (!io::CopyInputStreamToArchive(context, &manifest_buffer_in, path, compression_flags,
+ writer)) {
+ return false;
+ }
} else {
uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) {
@@ -128,4 +148,26 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
return true;
}
+std::unique_ptr<xml::XmlResource> LoadedApk::InflateManifest(IAaptContext* context) {
+ IDiagnostics* diag = context->GetDiagnostics();
+
+ io::IFile* manifest_file = GetFileCollection()->FindFile("AndroidManifest.xml");
+ if (manifest_file == nullptr) {
+ diag->Error(DiagMessage(source_) << "no AndroidManifest.xml found");
+ return {};
+ }
+
+ std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
+ if (manifest_data == nullptr) {
+ diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml");
+ return {};
+ }
+
+ std::unique_ptr<xml::XmlResource> manifest =
+ xml::Inflate(manifest_data->data(), manifest_data->size(), diag, manifest_file->GetSource());
+ if (manifest == nullptr) {
+ diag->Error(DiagMessage() << "failed to read binary AndroidManifest.xml");
+ }
+ return manifest;
+}
} // namespace aapt
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index 8aa9674aa2ed..d2dd5cf2bc67 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -21,14 +21,15 @@
#include "ResourceTable.h"
#include "filter/Filter.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/BinaryResourceParser.h"
+#include "format/binary/TableFlattener.h"
#include "io/ZipArchive.h"
-#include "unflatten/BinaryResourceParser.h"
+#include "xml/XmlDom.h"
namespace aapt {
-/** Info about an APK loaded in memory. */
+// Info about an APK loaded in memory.
class LoadedApk {
public:
LoadedApk(
@@ -36,37 +37,52 @@ class LoadedApk {
std::unique_ptr<io::IFileCollection> apk,
std::unique_ptr<ResourceTable> table)
: source_(source), apk_(std::move(apk)), table_(std::move(table)) {}
+ virtual ~LoadedApk() = default;
- io::IFileCollection* GetFileCollection() { return apk_.get(); }
+ io::IFileCollection* GetFileCollection() {
+ return apk_.get();
+ }
- ResourceTable* GetResourceTable() { return table_.get(); }
+ ResourceTable* GetResourceTable() {
+ return table_.get();
+ }
- const Source& GetSource() { return source_; }
+ const Source& GetSource() {
+ return source_;
+ }
/**
* Writes the APK on disk at the given path, while also removing the resource
* files that are not referenced in the resource table.
*/
- bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
- IArchiveWriter* writer);
+ virtual bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
+ IArchiveWriter* writer);
/**
- * Writes the APK on disk at the given path, while also removing the resource
- * files that are not referenced in the resource table. The provided filter
- * chain is applied to each entry in the APK file.
+ * Writes the APK on disk at the given path, while also removing the resource files that are not
+ * referenced in the resource table. The provided filter chain is applied to each entry in the APK
+ * file.
+ *
+ * If the manifest is also provided, it will be written to the new APK file, otherwise the
+ * original manifest will be written. The manifest is only required if the contents of the new APK
+ * have been modified in a way that require the AndroidManifest.xml to also be modified.
*/
- bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
- FilterChain* filters, IArchiveWriter* writer);
+ virtual bool WriteToArchive(IAaptContext* context, ResourceTable* split_table,
+ const TableFlattenerOptions& options, FilterChain* filters,
+ IArchiveWriter* writer, xml::XmlResource* manifest = nullptr);
+
+ /** Inflates the AndroidManifest.xml file from the APK. */
+ std::unique_ptr<xml::XmlResource> InflateManifest(IAaptContext* context);
static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context,
const android::StringPiece& path);
private:
+ DISALLOW_COPY_AND_ASSIGN(LoadedApk);
+
Source source_;
std::unique_ptr<io::IFileCollection> apk_;
std::unique_ptr<ResourceTable> table_;
-
- DISALLOW_COPY_AND_ASSIGN(LoadedApk);
};
} // namespace aapt
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index 7664fac44be1..d81921f23904 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -24,9 +24,10 @@
#include "util/Util.h"
-namespace aapt {
+using ::android::ResTable_config;
+using ::android::StringPiece;
-using android::ResTable_config;
+namespace aapt {
void LocaleValue::set_language(const char* language_chars) {
size_t i = 0;
@@ -72,7 +73,7 @@ static inline bool is_number(const std::string& str) {
return std::all_of(std::begin(str), std::end(str), ::isdigit);
}
-bool LocaleValue::InitFromFilterString(const android::StringPiece& str) {
+bool LocaleValue::InitFromFilterString(const StringPiece& str) {
// A locale (as specified in the filter) is an underscore separated name such
// as "en_US", "en_Latn_US", or "en_US_POSIX".
std::vector<std::string> parts = util::SplitAndLowercase(str, '_');
@@ -138,6 +139,71 @@ bool LocaleValue::InitFromFilterString(const android::StringPiece& str) {
return true;
}
+bool LocaleValue::InitFromBcp47Tag(const StringPiece& bcp47tag) {
+ return InitFromBcp47TagImpl(bcp47tag, '-');
+}
+
+bool LocaleValue::InitFromBcp47TagImpl(const StringPiece& bcp47tag, const char separator) {
+ std::vector<std::string> subtags = util::SplitAndLowercase(bcp47tag, separator);
+ if (subtags.size() == 1) {
+ set_language(subtags[0].c_str());
+ } else if (subtags.size() == 2) {
+ set_language(subtags[0].c_str());
+
+ // The second tag can either be a region, a variant or a script.
+ switch (subtags[1].size()) {
+ case 2:
+ case 3:
+ set_region(subtags[1].c_str());
+ break;
+ case 4:
+ if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
+ // This is a variant: fall through
+ } else {
+ set_script(subtags[1].c_str());
+ break;
+ }
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ set_variant(subtags[1].c_str());
+ break;
+ default:
+ return false;
+ }
+ } else if (subtags.size() == 3) {
+ // The language is always the first subtag.
+ set_language(subtags[0].c_str());
+
+ // The second subtag can either be a script or a region code.
+ // If its size is 4, it's a script code, else it's a region code.
+ if (subtags[1].size() == 4) {
+ set_script(subtags[1].c_str());
+ } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
+ set_region(subtags[1].c_str());
+ } else {
+ return false;
+ }
+
+ // The third tag can either be a region code (if the second tag was
+ // a script), else a variant code.
+ if (subtags[2].size() >= 4) {
+ set_variant(subtags[2].c_str());
+ } else {
+ set_region(subtags[2].c_str());
+ }
+ } else if (subtags.size() == 4) {
+ set_language(subtags[0].c_str());
+ set_script(subtags[1].c_str());
+ set_region(subtags[2].c_str());
+ set_variant(subtags[3].c_str());
+ } else {
+ return false;
+ }
+ return true;
+}
+
ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
std::vector<std::string>::iterator end) {
const std::vector<std::string>::iterator start_iter = iter;
@@ -145,71 +211,13 @@ ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
std::string& part = *iter;
if (part[0] == 'b' && part[1] == '+') {
// This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
- // except that the separator is "+" and not "-".
- std::vector<std::string> subtags = util::SplitAndLowercase(part, '+');
- subtags.erase(subtags.begin());
- if (subtags.size() == 1) {
- set_language(subtags[0].c_str());
- } else if (subtags.size() == 2) {
- set_language(subtags[0].c_str());
-
- // The second tag can either be a region, a variant or a script.
- switch (subtags[1].size()) {
- case 2:
- case 3:
- set_region(subtags[1].c_str());
- break;
- case 4:
- if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
- // This is a variant: fall through
- } else {
- set_script(subtags[1].c_str());
- break;
- }
- case 5:
- case 6:
- case 7:
- case 8:
- set_variant(subtags[1].c_str());
- break;
- default:
- return -1;
- }
- } else if (subtags.size() == 3) {
- // The language is always the first subtag.
- set_language(subtags[0].c_str());
-
- // The second subtag can either be a script or a region code.
- // If its size is 4, it's a script code, else it's a region code.
- if (subtags[1].size() == 4) {
- set_script(subtags[1].c_str());
- } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
- set_region(subtags[1].c_str());
- } else {
- return -1;
- }
-
- // The third tag can either be a region code (if the second tag was
- // a script), else a variant code.
- if (subtags[2].size() >= 4) {
- set_variant(subtags[2].c_str());
- } else {
- set_region(subtags[2].c_str());
- }
- } else if (subtags.size() == 4) {
- set_language(subtags[0].c_str());
- set_script(subtags[1].c_str());
- set_region(subtags[2].c_str());
- set_variant(subtags[3].c_str());
- } else {
+ // except that the separator is "+" and not "-". Skip the prefix 'b+'.
+ if (!InitFromBcp47TagImpl(StringPiece(part).substr(2), '+')) {
return -1;
}
-
++iter;
-
} else {
- if ((part.length() == 2 || part.length() == 3) && is_alpha(part) &&
- part != "car") {
+ if ((part.length() == 2 || part.length() == 3) && is_alpha(part) && part != "car") {
set_language(part.c_str());
++iter;
@@ -222,7 +230,6 @@ ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
}
}
}
-
return static_cast<ssize_t>(iter - start_iter);
}
diff --git a/tools/aapt2/Locale.h b/tools/aapt2/Locale.h
index 3d73b2eb17bf..6d8b598415cc 100644
--- a/tools/aapt2/Locale.h
+++ b/tools/aapt2/Locale.h
@@ -41,6 +41,9 @@ struct LocaleValue {
*/
bool InitFromFilterString(const android::StringPiece& config);
+ // Initializes this LocaleValue from a BCP-47 locale tag.
+ bool InitFromBcp47Tag(const android::StringPiece& bcp47tag);
+
/**
* Initialize this LocaleValue from parts of a vector.
*/
@@ -67,6 +70,8 @@ struct LocaleValue {
inline bool operator>(const LocaleValue& o) const;
private:
+ bool InitFromBcp47TagImpl(const android::StringPiece& bcp47tag, const char separator);
+
void set_language(const char* language);
void set_region(const char* language);
void set_script(const char* script);
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index 1305a4cf0710..f1aad29a5c58 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -69,8 +69,7 @@ class NameMangler {
* The mangled name should contain symbols that are illegal to define in XML,
* so that there will never be name mangling collisions.
*/
- static std::string MangleEntry(const std::string& package,
- const std::string& name) {
+ static std::string MangleEntry(const std::string& package, const std::string& name) {
return package + "$" + name;
}
@@ -86,8 +85,8 @@ class NameMangler {
}
out_package->assign(out_name->data(), pivot);
- out_name->assign(out_name->data() + pivot + 1,
- out_name->size() - (pivot + 1));
+ std::string new_name = out_name->substr(pivot + 1);
+ *out_name = std::move(new_name);
return true;
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1c3ac2ad4f17..4cc60a8dbb07 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -392,6 +392,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
{"declare-styleable", std::mem_fn(&ResourceParser::ParseDeclareStyleable)},
{"integer-array", std::mem_fn(&ResourceParser::ParseIntegerArray)},
{"java-symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
+ {"overlayable", std::mem_fn(&ResourceParser::ParseOverlayable)},
{"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
{"public", std::mem_fn(&ResourceParser::ParsePublic)},
{"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
@@ -498,7 +499,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
const auto bag_iter = elToBagMap.find(resource_type);
if (bag_iter != elToBagMap.end()) {
// Ensure we have a name (unless this is a <public-group>).
- if (resource_type != "public-group") {
+ if (resource_type != "public-group" && resource_type != "overlayable") {
if (!maybe_name) {
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name() << "> missing 'name' attribute");
@@ -605,7 +606,7 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
if (processed_item) {
// Fix up the reference.
if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
- TransformReferenceFromNamespace(parser, "", ref);
+ ResolvePackage(parser, ref);
}
return processed_item;
}
@@ -690,6 +691,11 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser,
bool ResourceParser::ParsePublic(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
+ if (out_resource->config != ConfigDescription::DefaultConfig()) {
+ diag_->Warn(DiagMessage(out_resource->source)
+ << "ignoring configuration '" << out_resource->config << "' for <public> tag");
+ }
+
Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
diag_->Error(DiagMessage(out_resource->source)
@@ -726,8 +732,13 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser,
return true;
}
-bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser,
- ParsedResource* out_resource) {
+bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+ if (out_resource->config != ConfigDescription::DefaultConfig()) {
+ diag_->Warn(DiagMessage(out_resource->source)
+ << "ignoring configuration '" << out_resource->config
+ << "' for <public-group> tag");
+ }
+
Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
diag_->Error(DiagMessage(out_resource->source)
@@ -842,13 +853,83 @@ bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
return true;
}
-bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser,
- ParsedResource* out_resource) {
- if (ParseSymbolImpl(parser, out_resource)) {
- out_resource->symbol_state = SymbolState::kPrivate;
- return true;
+bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+ if (out_resource->config != ConfigDescription::DefaultConfig()) {
+ diag_->Warn(DiagMessage(out_resource->source)
+ << "ignoring configuration '" << out_resource->config << "' for <"
+ << parser->element_name() << "> tag");
}
- return false;
+
+ if (!ParseSymbolImpl(parser, out_resource)) {
+ return false;
+ }
+
+ out_resource->symbol_state = SymbolState::kPrivate;
+ return true;
+}
+
+bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+ if (out_resource->config != ConfigDescription::DefaultConfig()) {
+ diag_->Warn(DiagMessage(out_resource->source)
+ << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag");
+ }
+
+ if (Maybe<StringPiece> maybe_policy = xml::FindNonEmptyAttribute(parser, "policy")) {
+ const StringPiece& policy = maybe_policy.value();
+ if (policy != "system") {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<overlayable> has invalid policy '" << policy << "'");
+ return false;
+ }
+ }
+
+ bool error = false;
+ const size_t depth = parser->depth();
+ while (xml::XmlPullParser::NextChildNode(parser, depth)) {
+ if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text/comments.
+ continue;
+ }
+
+ const Source item_source = source_.WithLine(parser->line_number());
+ const std::string& element_namespace = parser->element_namespace();
+ const std::string& element_name = parser->element_name();
+ if (element_namespace.empty() && element_name == "item") {
+ Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(item_source)
+ << "<item> within an <overlayable> tag must have a 'name' attribute");
+ error = true;
+ continue;
+ }
+
+ Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ if (!maybe_type) {
+ diag_->Error(DiagMessage(item_source)
+ << "<item> within an <overlayable> tag must have a 'type' attribute");
+ error = true;
+ continue;
+ }
+
+ const ResourceType* type = ParseResourceType(maybe_type.value());
+ if (type == nullptr) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "invalid resource type '" << maybe_type.value()
+ << "' in <item> within an <overlayable>");
+ error = true;
+ continue;
+ }
+
+ // TODO(b/64980941): Mark the symbol as overlayable and allow marking which entity can overlay
+ // the resource (system/app).
+
+ xml::XmlPullParser::SkipCurrentElement(parser);
+ } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
+ diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
+ error = true;
+ }
+ }
+ return !error;
}
bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser,
@@ -1074,15 +1155,13 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
return false;
}
- Maybe<Reference> maybe_key =
- ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+ Maybe<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
if (!maybe_key) {
- diag_->Error(DiagMessage(source) << "invalid attribute name '"
- << maybe_name.value() << "'");
+ diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'");
return false;
}
- TransformReferenceFromNamespace(parser, "", &maybe_key.value());
+ ResolvePackage(parser, &maybe_key.value());
maybe_key.value().SetSource(source);
std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
@@ -1091,8 +1170,7 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
return false;
}
- style->entries.push_back(
- Style::Entry{std::move(maybe_key.value()), std::move(value)});
+ style->entries.push_back(Style::Entry{std::move(maybe_key.value()), std::move(value)});
return true;
}
@@ -1104,21 +1182,18 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
if (maybe_parent) {
- // If the parent is empty, we don't have a parent, but we also don't infer
- // either.
+ // If the parent is empty, we don't have a parent, but we also don't infer either.
if (!maybe_parent.value().empty()) {
std::string err_str;
- style->parent = ResourceUtils::ParseStyleParentReference(
- maybe_parent.value(), &err_str);
+ style->parent = ResourceUtils::ParseStyleParentReference(maybe_parent.value(), &err_str);
if (!style->parent) {
diag_->Error(DiagMessage(out_resource->source) << err_str);
return false;
}
- // Transform the namespace prefix to the actual package name, and mark the
- // reference as
+ // Transform the namespace prefix to the actual package name, and mark the reference as
// private if appropriate.
- TransformReferenceFromNamespace(parser, "", &style->parent.value());
+ ResolvePackage(parser, &style->parent.value());
}
} else {
@@ -1127,8 +1202,7 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
size_t pos = style_name.find_last_of(u'.');
if (pos != std::string::npos) {
style->parent_inferred = true;
- style->parent = Reference(
- ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
+ style->parent = Reference(ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
}
}
@@ -1373,7 +1447,7 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
}
Reference& child_ref = maybe_ref.value();
- xml::TransformReferenceFromNamespace(parser, "", &child_ref);
+ xml::ResolvePackage(parser, &child_ref);
// Create the ParsedResource that will add the attribute to the table.
ParsedResource child_resource;
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 5631dc2ad29c..fb9dbd0cd0fd 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -91,6 +91,7 @@ class ResourceParser {
bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 144ebd22e105..f08b03e7b8af 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -790,4 +790,49 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) {
ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)"));
}
+TEST_F(ResourceParserTest, ParseOverlayableTagWithSystemPolicy) {
+ std::string input = R"(
+ <overlayable policy="illegal_policy">
+ <item type="string" name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable policy="system">
+ <item name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable policy="system">
+ <item type="attr" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(
+ <overlayable policy="system">
+ <item type="bad_type" name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
+
+ input = R"(<overlayable policy="system" />)";
+ EXPECT_TRUE(TestParse(input));
+
+ input = R"(<overlayable />)";
+ EXPECT_TRUE(TestParse(input));
+
+ input = R"(
+ <overlayable policy="system">
+ <item type="string" name="foo" />
+ <item type="dimen" name="foo" />
+ </overlayable>)";
+ ASSERT_TRUE(TestParse(input));
+
+ input = R"(
+ <overlayable>
+ <item type="string" name="bar" />
+ </overlayable>)";
+ ASSERT_TRUE(TestParse(input));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index ab59560d33a3..0304e21698df 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -546,4 +546,34 @@ Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNam
return SearchResult{package, type, entry};
}
+std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
+ std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
+ for (const auto& pkg : packages) {
+ ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
+ for (const auto& type : pkg->types) {
+ ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
+ if (!new_type->id) {
+ new_type->id = type->id;
+ new_type->symbol_status = type->symbol_status;
+ }
+
+ for (const auto& entry : type->entries) {
+ ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
+ if (!new_entry->id) {
+ new_entry->id = entry->id;
+ new_entry->symbol_status = entry->symbol_status;
+ }
+
+ for (const auto& config_value : entry->values) {
+ ResourceConfigValue* new_value =
+ new_entry->FindOrCreateValue(config_value->config, config_value->product);
+ Value* value = config_value->value->Clone(&new_table->string_pool);
+ new_value->value = std::unique_ptr<Value>(value);
+ }
+ }
+ }
+ }
+ return new_table;
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 4295d0674774..d5db67e77f51 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -251,6 +251,8 @@ class ResourceTable {
ResourceTablePackage* CreatePackage(const android::StringPiece& name, Maybe<uint8_t> id = {});
+ std::unique_ptr<ResourceTable> Clone() const;
+
/**
* The string pool used by this resource table. Values that reference strings
* must use
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index f193fe0c6593..6fac6e9dfefe 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -23,12 +23,12 @@
#include "NameMangler.h"
#include "SdkConstants.h"
-#include "flatten/ResourceTypeExtensions.h"
+#include "format/binary/ResourceTypeExtensions.h"
#include "util/Files.h"
#include "util/Util.h"
-using android::StringPiece;
-using android::StringPiece16;
+using ::android::StringPiece;
+using ::android::StringPiece16;
namespace aapt {
namespace ResourceUtils {
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 1cba19462839..e0137290f5ee 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -35,15 +35,25 @@ std::ostream& operator<<(std::ostream& out, const Value& value) {
}
template <typename Derived>
-void BaseValue<Derived>::Accept(RawValueVisitor* visitor) {
+void BaseValue<Derived>::Accept(ValueVisitor* visitor) {
visitor->Visit(static_cast<Derived*>(this));
}
template <typename Derived>
-void BaseItem<Derived>::Accept(RawValueVisitor* visitor) {
+void BaseValue<Derived>::Accept(ConstValueVisitor* visitor) const {
+ visitor->Visit(static_cast<const Derived*>(this));
+}
+
+template <typename Derived>
+void BaseItem<Derived>::Accept(ValueVisitor* visitor) {
visitor->Visit(static_cast<Derived*>(this));
}
+template <typename Derived>
+void BaseItem<Derived>::Accept(ConstValueVisitor* visitor) const {
+ visitor->Visit(static_cast<const Derived*>(this));
+}
+
RawString::RawString(const StringPool::Ref& ref) : value(ref) {}
bool RawString::Equals(const Value* value) const {
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 275864bbcd3e..742765d166da 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -33,7 +33,8 @@
namespace aapt {
-struct RawValueVisitor;
+class ValueVisitor;
+class ConstValueVisitor;
// A resource value. This is an all-encompassing representation
// of Item and Map and their subclasses. The way to do
@@ -45,36 +46,58 @@ class Value {
virtual ~Value() = default;
// Whether this value is weak and can be overridden without warning or error. Default is false.
- bool IsWeak() const { return weak_; }
+ bool IsWeak() const {
+ return weak_;
+ }
- void SetWeak(bool val) { weak_ = val; }
+ void SetWeak(bool val) {
+ weak_ = val;
+ }
- // Whether the value is marked as translatable.
- // This does not persist when flattened.
+ // Whether the value is marked as translatable. This does not persist when flattened to binary.
// It is only used during compilation phase.
- void SetTranslatable(bool val) { translatable_ = val; }
+ void SetTranslatable(bool val) {
+ translatable_ = val;
+ }
// Default true.
- bool IsTranslatable() const { return translatable_; }
+ bool IsTranslatable() const {
+ return translatable_;
+ }
// Returns the source where this value was defined.
- const Source& GetSource() const { return source_; }
+ const Source& GetSource() const {
+ return source_;
+ }
- void SetSource(const Source& source) { source_ = source; }
+ void SetSource(const Source& source) {
+ source_ = source;
+ }
- void SetSource(Source&& source) { source_ = std::move(source); }
+ void SetSource(Source&& source) {
+ source_ = std::move(source);
+ }
// Returns the comment that was associated with this resource.
- const std::string& GetComment() const { return comment_; }
+ const std::string& GetComment() const {
+ return comment_;
+ }
- void SetComment(const android::StringPiece& str) { comment_ = str.to_string(); }
+ void SetComment(const android::StringPiece& str) {
+ comment_ = str.to_string();
+ }
- void SetComment(std::string&& str) { comment_ = std::move(str); }
+ void SetComment(std::string&& str) {
+ comment_ = std::move(str);
+ }
virtual bool Equals(const Value* value) const = 0;
// Calls the appropriate overload of ValueVisitor.
- virtual void Accept(RawValueVisitor* visitor) = 0;
+ virtual void Accept(ValueVisitor* visitor) = 0;
+
+ // Calls the appropriate overload of ConstValueVisitor.
+ virtual void Accept(ConstValueVisitor* visitor) const = 0;
// Clone the value. `new_pool` is the new StringPool that
// any resources with strings should use when copying their string.
@@ -95,7 +118,8 @@ class Value {
// Inherit from this to get visitor accepting implementations for free.
template <typename Derived>
struct BaseValue : public Value {
- void Accept(RawValueVisitor* visitor) override;
+ void Accept(ValueVisitor* visitor) override;
+ void Accept(ConstValueVisitor* visitor) const override;
};
// A resource item with a single value. This maps to android::ResTable_entry.
@@ -111,7 +135,8 @@ struct Item : public Value {
// Inherit from this to get visitor accepting implementations for free.
template <typename Derived>
struct BaseItem : public Item {
- void Accept(RawValueVisitor* visitor) override;
+ void Accept(ValueVisitor* visitor) override;
+ void Accept(ConstValueVisitor* visitor) const override;
};
// A reference to another resource. This maps to android::Res_value::TYPE_REFERENCE.
@@ -144,7 +169,10 @@ bool operator==(const Reference&, const Reference&);
// An ID resource. Has no real value, just a place holder.
struct Id : public BaseItem<Id> {
- Id() { weak_ = true; }
+ Id() {
+ weak_ = true;
+ }
+
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out) const override;
Id* Clone(StringPool* new_pool) const override;
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 71f33b0853ad..174b7f6b7c8c 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -14,77 +14,80 @@
* limitations under the License.
*/
-// Keep proto2 syntax because we require the distinction between fields that
-// are set and unset.
-syntax = "proto2";
+syntax = "proto3";
-option java_package = "com.android.aapt";
-option optimize_for = LITE_RUNTIME;
+import "frameworks/base/tools/aapt2/Configuration.proto";
package aapt.pb;
-// A configuration description that wraps the binary form of the C++ class
-// aapt::ConfigDescription, with an added product definition.
-// TODO(adamlesinski): Flesh this out to be represented in proto.
-message ConfigDescription {
- optional bytes data = 1;
- optional string product = 2;
-}
+option java_package = "com.android.aapt";
+option optimize_for = LITE_RUNTIME;
// A string pool that wraps the binary form of the C++ class android::ResStringPool.
message StringPool {
- optional bytes data = 1;
+ bytes data = 1;
}
// The position of a declared entity within a file.
message SourcePosition {
- optional uint32 line_number = 1;
- optional uint32 column_number = 2;
+ uint32 line_number = 1;
+ uint32 column_number = 2;
}
// Developer friendly source file information for an entity in the resource table.
message Source {
// The index of the string path within the source string pool of a ResourceTable.
- optional uint32 path_idx = 1;
- optional SourcePosition position = 2;
+ uint32 path_idx = 1;
+ SourcePosition position = 2;
}
// Top level message representing a resource table.
message ResourceTable {
// The string pool containing source paths referenced throughout the resource table. This does
// not end up in the final binary ARSC file.
- optional StringPool source_pool = 1;
+ StringPool source_pool = 1;
// Resource definitions corresponding to an Android package.
repeated Package package = 2;
}
+// A package ID in the range [0x00, 0xff].
+message PackageId {
+ uint32 id = 1;
+}
+
// Defines resources for an Android package.
message Package {
// The package ID of this package, in the range [0x00, 0xff].
- // The ID 0x00 is reserved for shared libraries, or when the ID is assigned at run-time.
- // The ID 0x01 is reserved for the 'android' package (framework).
- // The ID range [0x02, 0x7f) is reserved for auto-assignment to shared libraries at run-time.
- // The ID 0x7f is reserved for the application package.
- // IDs > 0x7f are reserved for the application as well and are treated as feature splits.
- optional uint32 package_id = 1;
+ // - ID 0x00 is reserved for shared libraries, or when the ID is assigned at run-time.
+ // - ID 0x01 is reserved for the 'android' package (framework).
+ // - ID range [0x02, 0x7f) is reserved for auto-assignment to shared libraries at run-time.
+ // - ID 0x7f is reserved for the application package.
+ // - IDs > 0x7f are reserved for the application as well and are treated as feature splits.
+ // This may not be set if no ID was assigned.
+ PackageId package_id = 1;
// The Java compatible Android package name of the app.
- optional string package_name = 2;
+ string package_name = 2;
// The series of types defined by the package.
repeated Type type = 3;
}
+// A type ID in the range [0x01, 0xff].
+message TypeId {
+ uint32 id = 1;
+}
+
// A set of resources grouped under a common type. Such types include string, layout, xml, dimen,
// attr, etc. This maps to the second part of a resource identifier in Java (R.type.entry).
message Type {
- // The ID of the type. This may be 0, which indicates no ID is set.
- optional uint32 id = 1;
+ // The ID of the type. This may not be set if no ID was assigned.
+ TypeId type_id = 1;
// The name of the type. This corresponds to the 'type' part of a full resource name of the form
// package:type/entry. The set of legal type names is listed in Resource.cpp.
- optional string name = 2;
+ string name = 2;
// The entries defined for this type.
repeated Entry entry = 3;
@@ -112,17 +115,22 @@ message SymbolStatus {
PUBLIC = 2;
}
- optional Visibility visibility = 1;
+ Visibility visibility = 1;
// The path at which this entry's visibility was defined (eg. public.xml).
- optional Source source = 2;
+ Source source = 2;
// The comment associated with the <public> tag.
- optional string comment = 3;
+ string comment = 3;
// Whether the symbol can be merged into another resource table without there being an existing
// definition to override. Used for overlays and set to true when <add-resource> is specified.
- optional bool allow_new = 4;
+ bool allow_new = 4;
+}
+
+// An entry ID in the range [0x0000, 0xffff].
+message EntryId {
+ uint32 id = 1;
}
// An entry declaration. An entry has a full resource ID that is the combination of package ID,
@@ -132,14 +140,15 @@ message Entry {
// The ID of this entry. Together with the package ID and type ID, this forms a full resource ID
// of the form 0xPPTTEEEE, where PP is the package ID, TT is the type ID, and EEEE is the entry
// ID.
- optional uint32 id = 1;
+ // This may not be set if no ID was assigned.
+ EntryId entry_id = 1;
// The name of this entry. This corresponds to the 'entry' part of a full resource name of the
// form package:type/entry.
- optional string name = 2;
+ string name = 2;
// The symbol status of this entry, which includes visibility information.
- optional SymbolStatus symbol_status = 3;
+ SymbolStatus symbol_status = 3;
// The set of values defined for this entry, each corresponding to a different
// configuration/variant.
@@ -148,50 +157,54 @@ message Entry {
// A Configuration/Value pair.
message ConfigValue {
- optional ConfigDescription config = 1;
- optional Value value = 2;
+ Configuration config = 1;
+ Value value = 2;
}
// The generic meta-data for every value in a resource table.
message Value {
// Where the value was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comment associated with the value.
- optional string comment = 2;
+ string comment = 2;
// Whether the value can be overridden.
- optional bool weak = 3;
+ bool weak = 3;
- // If the value is an Item, this is set.
- optional Item item = 4;
-
- // If the value is a CompoundValue, this is set.
- optional CompoundValue compound_value = 5;
+ // The value is either an Item or a CompoundValue.
+ oneof value {
+ Item item = 4;
+ CompoundValue compound_value = 5;
+ }
}
// An Item is an abstract type. It represents a value that can appear inline in many places, such
// as XML attribute values or on the right hand side of style attribute definitions. The concrete
// type is one of the types below. Only one can be set.
message Item {
- optional Reference ref = 1;
- optional String str = 2;
- optional RawString raw_str = 3;
- optional StyledString styled_str = 4;
- optional FileReference file = 5;
- optional Id id = 6;
- optional Primitive prim = 7;
+ oneof value {
+ Reference ref = 1;
+ String str = 2;
+ RawString raw_str = 3;
+ StyledString styled_str = 4;
+ FileReference file = 5;
+ Id id = 6;
+ Primitive prim = 7;
+ }
}
// A CompoundValue is an abstract type. It represents a value that is a made of other values.
// These can only usually appear as top-level resources. The concrete type is one of the types
// below. Only one can be set.
message CompoundValue {
- optional Attribute attr = 1;
- optional Style style = 2;
- optional Styleable styleable = 3;
- optional Array array = 4;
- optional Plural plural = 5;
+ oneof value {
+ Attribute attr = 1;
+ Style style = 2;
+ Styleable styleable = 3;
+ Array array = 4;
+ Plural plural = 5;
+ }
}
// A value that is a reference to another resource. This reference can be by name or resource ID.
@@ -204,16 +217,16 @@ message Reference {
ATTRIBUTE = 1;
}
- optional Type type = 1;
+ Type type = 1;
- // The resource ID (0xPPTTEEEE) of the resource being referred.
- optional uint32 id = 2;
+ // The resource ID (0xPPTTEEEE) of the resource being referred. This is optional.
+ uint32 id = 2;
- // The optional resource name.
- optional string name = 3;
+ // The name of the resource being referred. This is optional if the resource ID is set.
+ string name = 3;
// Whether this reference is referencing a private resource (@*package:type/entry).
- optional bool private = 4;
+ bool private = 4;
}
// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
@@ -223,32 +236,32 @@ message Id {
// A value that is a string.
message String {
- optional string value = 1;
+ string value = 1;
}
// A value that is a raw string, which is unescaped/uninterpreted. This is typically used to
// represent the value of a style attribute before the attribute is compiled and the set of
// allowed values is known.
message RawString {
- optional string value = 1;
+ string value = 1;
}
// A string with styling information, like html tags that specify boldness, italics, etc.
message StyledString {
// The raw text of the string.
- optional string value = 1;
+ string value = 1;
// A Span marks a region of the string text that is styled.
message Span {
// The name of the tag, and its attributes, encoded as follows:
// tag_name;attr1=value1;attr2=value2;[...]
- optional string tag = 1;
+ string tag = 1;
// The first character position this span applies to, in UTF-16 offset.
- optional uint32 first_char = 2;
+ uint32 first_char = 2;
// The last character position this span applies to, in UTF-16 offset.
- optional uint32 last_char = 3;
+ uint32 last_char = 3;
}
repeated Span span = 2;
@@ -257,14 +270,14 @@ message StyledString {
// A value that is a reference to an external entity, like an XML file or a PNG.
message FileReference {
// Path to a file within the APK (typically res/type-config/entry.ext).
- optional string path = 1;
+ string path = 1;
}
// A value that represents a primitive data type (float, int, boolean, etc.).
// Corresponds to the fields (type/data) of the C struct android::Res_value.
message Primitive {
- optional uint32 type = 1;
- optional uint32 data = 2;
+ uint32 type = 1;
+ uint32 data = 2;
}
// A value that represents an XML attribute and what values it accepts.
@@ -272,21 +285,22 @@ message Attribute {
// A Symbol used to represent an enum or a flag.
message Symbol {
// Where the enum/flag item was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the enum or flag.
- optional string comment = 2;
+ string comment = 2;
// The name of the enum/flag as a reference. Enums/flag items are generated as ID resource
// values.
- optional Reference name = 3;
+ Reference name = 3;
// The value of the enum/flag.
- optional uint32 value = 4;
+ uint32 value = 4;
}
// Bitmask of formats allowed for an attribute.
enum FormatFlags {
+ NONE = 0x0; // Proto3 requires a default of 0.
ANY = 0x0000ffff; // Allows any type except ENUM and FLAGS.
REFERENCE = 0x01; // Allows Reference values.
STRING = 0x02; // Allows String/StyledString values.
@@ -304,15 +318,15 @@ message Attribute {
// A bitmask of types that this XML attribute accepts. Corresponds to the flags in the
// enum FormatFlags.
- optional uint32 format_flags = 1;
+ uint32 format_flags = 1;
// The smallest integer allowed for this XML attribute. Only makes sense if the format includes
// FormatFlags::INTEGER.
- optional int32 min_int = 2;
+ int32 min_int = 2;
// The largest integer allowed for this XML attribute. Only makes sense if the format includes
// FormatFlags::INTEGER.
- optional int32 max_int = 3;
+ int32 max_int = 3;
// The set of enums/flags defined in this attribute. Only makes sense if the format includes
// either FormatFlags::ENUM or FormatFlags::FLAGS. Having both is an error.
@@ -324,23 +338,23 @@ message Style {
// An XML attribute/value pair defined in the style.
message Entry {
// Where the entry was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the entry.
- optional string comment = 2;
+ string comment = 2;
// A reference to the XML attribute.
- optional Reference key = 3;
+ Reference key = 3;
// The Item defined for this XML attribute.
- optional Item item = 4;
+ Item item = 4;
}
// The optinal style from which this style inherits attributes.
- optional Reference parent = 1;
+ Reference parent = 1;
// The source file information of the parent inheritance declaration.
- optional Source parent_source = 2;
+ Source parent_source = 2;
// The set of XML attribute/value pairs for this style.
repeated Entry entry = 3;
@@ -352,13 +366,13 @@ message Styleable {
// An attribute defined for this styleable.
message Entry {
// Where the attribute was defined within the <declare-styleable> block.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the declaration.
- optional string comment = 2;
+ string comment = 2;
// The reference to the attribute.
- optional Reference attr = 3;
+ Reference attr = 3;
}
// The set of attribute declarations.
@@ -370,13 +384,13 @@ message Array {
// A single element of the array.
message Element {
// Where the element was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the element.
- optional string comment = 2;
+ string comment = 2;
// The value assigned to this element.
- optional Item item = 3;
+ Item item = 3;
}
// The list of array elements.
@@ -398,16 +412,16 @@ message Plural {
// The plural value for a given arity.
message Entry {
// Where the plural was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the plural.
- optional string comment = 2;
+ string comment = 2;
// The arity of the plural.
- optional Arity arity = 3;
+ Arity arity = 3;
// The value assigned to this plural.
- optional Item item = 4;
+ Item item = 4;
}
// The set of arity/plural mappings.
@@ -417,14 +431,13 @@ message Plural {
// Defines an abstract XmlNode that must be either an XmlElement, or
// a text node represented by a string.
message XmlNode {
- // If set, this node is an element/tag.
- optional XmlElement element = 1;
-
- // If set, this node is a chunk of text.
- optional string text = 2;
+ oneof node {
+ XmlElement element = 1;
+ string text = 2;
+ }
// Source line and column info.
- optional SourcePosition source = 3;
+ SourcePosition source = 3;
}
// An <element> in an XML document.
@@ -433,10 +446,10 @@ message XmlElement {
repeated XmlNamespace namespace_declaration = 1;
// The namespace URI of this element.
- optional string namespace_uri = 2;
+ string namespace_uri = 2;
// The name of this element.
- optional string name = 3;
+ string name = 3;
// The attributes of this element.
repeated XmlAttribute attribute = 4;
@@ -447,25 +460,25 @@ message XmlElement {
// A namespace declaration on an XmlElement (xmlns:android="http://...").
message XmlNamespace {
- optional string prefix = 1;
- optional string uri = 2;
+ string prefix = 1;
+ string uri = 2;
// Source line and column info.
- optional SourcePosition source = 3;
+ SourcePosition source = 3;
}
// An attribute defined on an XmlElement (android:text="...").
message XmlAttribute {
- optional string namespace_uri = 1;
- optional string name = 2;
- optional string value = 3;
+ string namespace_uri = 1;
+ string name = 2;
+ string value = 3;
// Source line and column info.
- optional SourcePosition source = 4;
+ SourcePosition source = 4;
- // The resource ID (0xPPTTEEEE) of the attribute.
- optional uint32 resource_id = 5;
+ // The optional resource ID (0xPPTTEEEE) of the attribute.
+ uint32 resource_id = 5;
- // The interpreted/compiled version of the `value` string.
- optional Item compiled_item = 6;
+ // The optional interpreted/compiled version of the `value` string.
+ Item compiled_item = 6;
}
diff --git a/tools/aapt2/ResourcesInternal.proto b/tools/aapt2/ResourcesInternal.proto
index 31179174b843..0b0a252a3452 100644
--- a/tools/aapt2/ResourcesInternal.proto
+++ b/tools/aapt2/ResourcesInternal.proto
@@ -14,39 +14,40 @@
* limitations under the License.
*/
-syntax = "proto2";
-
-option java_package = "android.aapt.pb.internal";
-option optimize_for = LITE_RUNTIME;
+syntax = "proto3";
+import "frameworks/base/tools/aapt2/Configuration.proto";
import "frameworks/base/tools/aapt2/Resources.proto";
package aapt.pb.internal;
+option java_package = "android.aapt.pb.internal";
+option optimize_for = LITE_RUNTIME;
+
// The top level message representing an external resource file (layout XML, PNG, etc).
// This is used to represent a compiled file before it is linked. Only useful to aapt2.
message CompiledFile {
message Symbol {
// The name of the symbol (in the form package:type/name).
- optional string resource_name = 1;
+ string resource_name = 1;
// The position in the file at which this symbol is defined. For debug use.
- optional aapt.pb.SourcePosition source = 2;
+ aapt.pb.SourcePosition source = 2;
}
// The name of the resource (in the form package:type/name).
- optional string resource_name = 1;
+ string resource_name = 1;
// The configuration for which the resource is defined.
- optional aapt.pb.ConfigDescription config = 2;
+ aapt.pb.Configuration config = 2;
// The filesystem path to where the source file originated.
// Mainly used to display helpful error messages.
- optional string source_path = 3;
+ string source_path = 3;
// Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file).
repeated Symbol exported_symbol = 4;
// If this is a compiled XML file, this is the root node.
- optional aapt.pb.XmlNode xml_root = 5;
+ aapt.pb.XmlNode xml_root = 5;
}
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 041cb4fa96cd..8ebde752bc4b 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -25,8 +25,8 @@ using android::StringPiece;
namespace aapt {
-static const char* sDevelopmentSdkCodeName = "O";
-static ApiVersion sDevelopmentSdkLevel = 26;
+static const char* sDevelopmentSdkCodeName = "P";
+static ApiVersion sDevelopmentSdkLevel = 28;
static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
{0x021c, 1},
@@ -53,6 +53,7 @@ static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
{0x0527, SDK_NOUGAT},
{0x0530, SDK_NOUGAT_MR1},
{0x0568, SDK_O},
+ {0x056d, SDK_O_MR1},
};
static bool less_entry_id(const std::pair<uint16_t, ApiVersion>& p, uint16_t entryId) {
@@ -70,680 +71,6 @@ ApiVersion FindAttributeSdkLevel(const ResourceId& id) {
return iter->second;
}
-static const std::unordered_map<std::string, ApiVersion> sAttrMap = {
- {"marqueeRepeatLimit", 2},
- {"windowNoDisplay", 3},
- {"backgroundDimEnabled", 3},
- {"inputType", 3},
- {"isDefault", 3},
- {"windowDisablePreview", 3},
- {"privateImeOptions", 3},
- {"editorExtras", 3},
- {"settingsActivity", 3},
- {"fastScrollEnabled", 3},
- {"reqTouchScreen", 3},
- {"reqKeyboardType", 3},
- {"reqHardKeyboard", 3},
- {"reqNavigation", 3},
- {"windowSoftInputMode", 3},
- {"imeFullscreenBackground", 3},
- {"noHistory", 3},
- {"headerDividersEnabled", 3},
- {"footerDividersEnabled", 3},
- {"candidatesTextStyleSpans", 3},
- {"smoothScrollbar", 3},
- {"reqFiveWayNav", 3},
- {"keyBackground", 3},
- {"keyTextSize", 3},
- {"labelTextSize", 3},
- {"keyTextColor", 3},
- {"keyPreviewLayout", 3},
- {"keyPreviewOffset", 3},
- {"keyPreviewHeight", 3},
- {"verticalCorrection", 3},
- {"popupLayout", 3},
- {"state_long_pressable", 3},
- {"keyWidth", 3},
- {"keyHeight", 3},
- {"horizontalGap", 3},
- {"verticalGap", 3},
- {"rowEdgeFlags", 3},
- {"codes", 3},
- {"popupKeyboard", 3},
- {"popupCharacters", 3},
- {"keyEdgeFlags", 3},
- {"isModifier", 3},
- {"isSticky", 3},
- {"isRepeatable", 3},
- {"iconPreview", 3},
- {"keyOutputText", 3},
- {"keyLabel", 3},
- {"keyIcon", 3},
- {"keyboardMode", 3},
- {"isScrollContainer", 3},
- {"fillEnabled", 3},
- {"updatePeriodMillis", 3},
- {"initialLayout", 3},
- {"voiceSearchMode", 3},
- {"voiceLanguageModel", 3},
- {"voicePromptText", 3},
- {"voiceLanguage", 3},
- {"voiceMaxResults", 3},
- {"bottomOffset", 3},
- {"topOffset", 3},
- {"allowSingleTap", 3},
- {"handle", 3},
- {"content", 3},
- {"animateOnClick", 3},
- {"configure", 3},
- {"hapticFeedbackEnabled", 3},
- {"innerRadius", 3},
- {"thickness", 3},
- {"sharedUserLabel", 3},
- {"dropDownWidth", 3},
- {"dropDownAnchor", 3},
- {"imeOptions", 3},
- {"imeActionLabel", 3},
- {"imeActionId", 3},
- {"imeExtractEnterAnimation", 3},
- {"imeExtractExitAnimation", 3},
- {"tension", 4},
- {"extraTension", 4},
- {"anyDensity", 4},
- {"searchSuggestThreshold", 4},
- {"includeInGlobalSearch", 4},
- {"onClick", 4},
- {"targetSdkVersion", 4},
- {"maxSdkVersion", 4},
- {"testOnly", 4},
- {"contentDescription", 4},
- {"gestureStrokeWidth", 4},
- {"gestureColor", 4},
- {"uncertainGestureColor", 4},
- {"fadeOffset", 4},
- {"fadeDuration", 4},
- {"gestureStrokeType", 4},
- {"gestureStrokeLengthThreshold", 4},
- {"gestureStrokeSquarenessThreshold", 4},
- {"gestureStrokeAngleThreshold", 4},
- {"eventsInterceptionEnabled", 4},
- {"fadeEnabled", 4},
- {"backupAgent", 4},
- {"allowBackup", 4},
- {"glEsVersion", 4},
- {"queryAfterZeroResults", 4},
- {"dropDownHeight", 4},
- {"smallScreens", 4},
- {"normalScreens", 4},
- {"largeScreens", 4},
- {"progressBarStyleInverse", 4},
- {"progressBarStyleSmallInverse", 4},
- {"progressBarStyleLargeInverse", 4},
- {"searchSettingsDescription", 4},
- {"textColorPrimaryInverseDisableOnly", 4},
- {"autoUrlDetect", 4},
- {"resizeable", 4},
- {"required", 5},
- {"accountType", 5},
- {"contentAuthority", 5},
- {"userVisible", 5},
- {"windowShowWallpaper", 5},
- {"wallpaperOpenEnterAnimation", 5},
- {"wallpaperOpenExitAnimation", 5},
- {"wallpaperCloseEnterAnimation", 5},
- {"wallpaperCloseExitAnimation", 5},
- {"wallpaperIntraOpenEnterAnimation", 5},
- {"wallpaperIntraOpenExitAnimation", 5},
- {"wallpaperIntraCloseEnterAnimation", 5},
- {"wallpaperIntraCloseExitAnimation", 5},
- {"supportsUploading", 5},
- {"killAfterRestore", 5},
- {"restoreNeedsApplication", 5},
- {"smallIcon", 5},
- {"accountPreferences", 5},
- {"textAppearanceSearchResultSubtitle", 5},
- {"textAppearanceSearchResultTitle", 5},
- {"summaryColumn", 5},
- {"detailColumn", 5},
- {"detailSocialSummary", 5},
- {"thumbnail", 5},
- {"detachWallpaper", 5},
- {"finishOnCloseSystemDialogs", 5},
- {"scrollbarFadeDuration", 5},
- {"scrollbarDefaultDelayBeforeFade", 5},
- {"fadeScrollbars", 5},
- {"colorBackgroundCacheHint", 5},
- {"dropDownHorizontalOffset", 5},
- {"dropDownVerticalOffset", 5},
- {"quickContactBadgeStyleWindowSmall", 6},
- {"quickContactBadgeStyleWindowMedium", 6},
- {"quickContactBadgeStyleWindowLarge", 6},
- {"quickContactBadgeStyleSmallWindowSmall", 6},
- {"quickContactBadgeStyleSmallWindowMedium", 6},
- {"quickContactBadgeStyleSmallWindowLarge", 6},
- {"author", 7},
- {"autoStart", 7},
- {"expandableListViewWhiteStyle", 8},
- {"installLocation", 8},
- {"vmSafeMode", 8},
- {"webTextViewStyle", 8},
- {"restoreAnyVersion", 8},
- {"tabStripLeft", 8},
- {"tabStripRight", 8},
- {"tabStripEnabled", 8},
- {"logo", 9},
- {"xlargeScreens", 9},
- {"immersive", 9},
- {"overScrollMode", 9},
- {"overScrollHeader", 9},
- {"overScrollFooter", 9},
- {"filterTouchesWhenObscured", 9},
- {"textSelectHandleLeft", 9},
- {"textSelectHandleRight", 9},
- {"textSelectHandle", 9},
- {"textSelectHandleWindowStyle", 9},
- {"popupAnimationStyle", 9},
- {"screenSize", 9},
- {"screenDensity", 9},
- {"allContactsName", 11},
- {"windowActionBar", 11},
- {"actionBarStyle", 11},
- {"navigationMode", 11},
- {"displayOptions", 11},
- {"subtitle", 11},
- {"customNavigationLayout", 11},
- {"hardwareAccelerated", 11},
- {"measureWithLargestChild", 11},
- {"animateFirstView", 11},
- {"dropDownSpinnerStyle", 11},
- {"actionDropDownStyle", 11},
- {"actionButtonStyle", 11},
- {"showAsAction", 11},
- {"previewImage", 11},
- {"actionModeBackground", 11},
- {"actionModeCloseDrawable", 11},
- {"windowActionModeOverlay", 11},
- {"valueFrom", 11},
- {"valueTo", 11},
- {"valueType", 11},
- {"propertyName", 11},
- {"ordering", 11},
- {"fragment", 11},
- {"windowActionBarOverlay", 11},
- {"fragmentOpenEnterAnimation", 11},
- {"fragmentOpenExitAnimation", 11},
- {"fragmentCloseEnterAnimation", 11},
- {"fragmentCloseExitAnimation", 11},
- {"fragmentFadeEnterAnimation", 11},
- {"fragmentFadeExitAnimation", 11},
- {"actionBarSize", 11},
- {"imeSubtypeLocale", 11},
- {"imeSubtypeMode", 11},
- {"imeSubtypeExtraValue", 11},
- {"splitMotionEvents", 11},
- {"listChoiceBackgroundIndicator", 11},
- {"spinnerMode", 11},
- {"animateLayoutChanges", 11},
- {"actionBarTabStyle", 11},
- {"actionBarTabBarStyle", 11},
- {"actionBarTabTextStyle", 11},
- {"actionOverflowButtonStyle", 11},
- {"actionModeCloseButtonStyle", 11},
- {"titleTextStyle", 11},
- {"subtitleTextStyle", 11},
- {"iconifiedByDefault", 11},
- {"actionLayout", 11},
- {"actionViewClass", 11},
- {"activatedBackgroundIndicator", 11},
- {"state_activated", 11},
- {"listPopupWindowStyle", 11},
- {"popupMenuStyle", 11},
- {"textAppearanceLargePopupMen", 11},
- {"textAppearanceSmallPopupMen", 11},
- {"breadCrumbTitle", 11},
- {"breadCrumbShortTitle", 11},
- {"listDividerAlertDialog", 11},
- {"textColorAlertDialogListItem", 11},
- {"loopViews", 11},
- {"dialogTheme", 11},
- {"alertDialogTheme", 11},
- {"dividerVertical", 11},
- {"homeAsUpIndicator", 11},
- {"enterFadeDuration", 11},
- {"exitFadeDuration", 11},
- {"selectableItemBackground", 11},
- {"autoAdvanceViewId", 11},
- {"useIntrinsicSizeAsMinimum", 11},
- {"actionModeCutDrawable", 11},
- {"actionModeCopyDrawable", 11},
- {"actionModePasteDrawable", 11},
- {"textEditPasteWindowLayout", 11},
- {"textEditNoPasteWindowLayout", 11},
- {"textIsSelectable", 11},
- {"windowEnableSplitTouch", 11},
- {"indeterminateProgressStyle", 11},
- {"progressBarPadding", 11},
- {"animationResolution", 11},
- {"state_accelerated", 11},
- {"baseline", 11},
- {"homeLayout", 11},
- {"opacity", 11},
- {"alpha", 11},
- {"transformPivotX", 11},
- {"transformPivotY", 11},
- {"translationX", 11},
- {"translationY", 11},
- {"scaleX", 11},
- {"scaleY", 11},
- {"rotation", 11},
- {"rotationX", 11},
- {"rotationY", 11},
- {"showDividers", 11},
- {"dividerPadding", 11},
- {"borderlessButtonStyle", 11},
- {"dividerHorizontal", 11},
- {"itemPadding", 11},
- {"buttonBarStyle", 11},
- {"buttonBarButtonStyle", 11},
- {"segmentedButtonStyle", 11},
- {"staticWallpaperPreview", 11},
- {"allowParallelSyncs", 11},
- {"isAlwaysSyncable", 11},
- {"verticalScrollbarPosition", 11},
- {"fastScrollAlwaysVisible", 11},
- {"fastScrollThumbDrawable", 11},
- {"fastScrollPreviewBackgroundLeft", 11},
- {"fastScrollPreviewBackgroundRight", 11},
- {"fastScrollTrackDrawable", 11},
- {"fastScrollOverlayPosition", 11},
- {"customTokens", 11},
- {"nextFocusForward", 11},
- {"firstDayOfWeek", 11},
- {"showWeekNumber", 11},
- {"minDate", 11},
- {"maxDate", 11},
- {"shownWeekCount", 11},
- {"selectedWeekBackgroundColor", 11},
- {"focusedMonthDateColor", 11},
- {"unfocusedMonthDateColor", 11},
- {"weekNumberColor", 11},
- {"weekSeparatorLineColor", 11},
- {"selectedDateVerticalBar", 11},
- {"weekDayTextAppearance", 11},
- {"dateTextAppearance", 11},
- {"solidColor", 11},
- {"spinnersShown", 11},
- {"calendarViewShown", 11},
- {"state_multiline", 11},
- {"detailsElementBackground", 11},
- {"textColorHighlightInverse", 11},
- {"textColorLinkInverse", 11},
- {"editTextColor", 11},
- {"editTextBackground", 11},
- {"horizontalScrollViewStyle", 11},
- {"layerType", 11},
- {"alertDialogIcon", 11},
- {"windowMinWidthMajor", 11},
- {"windowMinWidthMinor", 11},
- {"queryHint", 11},
- {"fastScrollTextColor", 11},
- {"largeHeap", 11},
- {"windowCloseOnTouchOutside", 11},
- {"datePickerStyle", 11},
- {"calendarViewStyle", 11},
- {"textEditSidePasteWindowLayout", 11},
- {"textEditSideNoPasteWindowLayout", 11},
- {"actionMenuTextAppearance", 11},
- {"actionMenuTextColor", 11},
- {"textCursorDrawable", 12},
- {"resizeMode", 12},
- {"requiresSmallestWidthDp", 12},
- {"compatibleWidthLimitDp", 12},
- {"largestWidthLimitDp", 12},
- {"state_hovered", 13},
- {"state_drag_can_accept", 13},
- {"state_drag_hovered", 13},
- {"stopWithTask", 13},
- {"switchTextOn", 13},
- {"switchTextOff", 13},
- {"switchPreferenceStyle", 13},
- {"switchTextAppearance", 13},
- {"track", 13},
- {"switchMinWidth", 13},
- {"switchPadding", 13},
- {"thumbTextPadding", 13},
- {"textSuggestionsWindowStyle", 13},
- {"textEditSuggestionItemLayout", 13},
- {"rowCount", 13},
- {"rowOrderPreserved", 13},
- {"columnCount", 13},
- {"columnOrderPreserved", 13},
- {"useDefaultMargins", 13},
- {"alignmentMode", 13},
- {"layout_row", 13},
- {"layout_rowSpan", 13},
- {"layout_columnSpan", 13},
- {"actionModeSelectAllDrawable", 13},
- {"isAuxiliary", 13},
- {"accessibilityEventTypes", 13},
- {"packageNames", 13},
- {"accessibilityFeedbackType", 13},
- {"notificationTimeout", 13},
- {"accessibilityFlags", 13},
- {"canRetrieveWindowContent", 13},
- {"listPreferredItemHeightLarge", 13},
- {"listPreferredItemHeightSmall", 13},
- {"actionBarSplitStyle", 13},
- {"actionProviderClass", 13},
- {"backgroundStacked", 13},
- {"backgroundSplit", 13},
- {"textAllCaps", 13},
- {"colorPressedHighlight", 13},
- {"colorLongPressedHighlight", 13},
- {"colorFocusedHighlight", 13},
- {"colorActivatedHighlight", 13},
- {"colorMultiSelectHighlight", 13},
- {"drawableStart", 13},
- {"drawableEnd", 13},
- {"actionModeStyle", 13},
- {"minResizeWidth", 13},
- {"minResizeHeight", 13},
- {"actionBarWidgetTheme", 13},
- {"uiOptions", 13},
- {"subtypeLocale", 13},
- {"subtypeExtraValue", 13},
- {"actionBarDivider", 13},
- {"actionBarItemBackground", 13},
- {"actionModeSplitBackground", 13},
- {"textAppearanceListItem", 13},
- {"textAppearanceListItemSmall", 13},
- {"targetDescriptions", 13},
- {"directionDescriptions", 13},
- {"overridesImplicitlyEnabledSubtype", 13},
- {"listPreferredItemPaddingLeft", 13},
- {"listPreferredItemPaddingRight", 13},
- {"requiresFadingEdge", 13},
- {"publicKey", 13},
- {"parentActivityName", 16},
- {"isolatedProcess", 16},
- {"importantForAccessibility", 16},
- {"keyboardLayout", 16},
- {"fontFamily", 16},
- {"mediaRouteButtonStyle", 16},
- {"mediaRouteTypes", 16},
- {"supportsRtl", 17},
- {"textDirection", 17},
- {"textAlignment", 17},
- {"layoutDirection", 17},
- {"paddingStart", 17},
- {"paddingEnd", 17},
- {"layout_marginStart", 17},
- {"layout_marginEnd", 17},
- {"layout_toStartOf", 17},
- {"layout_toEndOf", 17},
- {"layout_alignStart", 17},
- {"layout_alignEnd", 17},
- {"layout_alignParentStart", 17},
- {"layout_alignParentEnd", 17},
- {"listPreferredItemPaddingStart", 17},
- {"listPreferredItemPaddingEnd", 17},
- {"singleUser", 17},
- {"presentationTheme", 17},
- {"subtypeId", 17},
- {"initialKeyguardLayout", 17},
- {"widgetCategory", 17},
- {"permissionGroupFlags", 17},
- {"labelFor", 17},
- {"permissionFlags", 17},
- {"checkedTextViewStyle", 17},
- {"showOnLockScreen", 17},
- {"format12Hour", 17},
- {"format24Hour", 17},
- {"timeZone", 17},
- {"mipMap", 18},
- {"mirrorForRtl", 18},
- {"windowOverscan", 18},
- {"requiredForAllUsers", 18},
- {"indicatorStart", 18},
- {"indicatorEnd", 18},
- {"childIndicatorStart", 18},
- {"childIndicatorEnd", 18},
- {"restrictedAccountType", 18},
- {"requiredAccountType", 18},
- {"canRequestTouchExplorationMode", 18},
- {"canRequestEnhancedWebAccessibility", 18},
- {"canRequestFilterKeyEvents", 18},
- {"layoutMode", 18},
- {"keySet", 19},
- {"targetId", 19},
- {"fromScene", 19},
- {"toScene", 19},
- {"transition", 19},
- {"transitionOrdering", 19},
- {"fadingMode", 19},
- {"startDelay", 19},
- {"ssp", 19},
- {"sspPrefix", 19},
- {"sspPattern", 19},
- {"addPrintersActivity", 19},
- {"vendor", 19},
- {"category", 19},
- {"isAsciiCapable", 19},
- {"autoMirrored", 19},
- {"supportsSwitchingToNextInputMethod", 19},
- {"requireDeviceUnlock", 19},
- {"apduServiceBanner", 19},
- {"accessibilityLiveRegion", 19},
- {"windowTranslucentStatus", 19},
- {"windowTranslucentNavigation", 19},
- {"advancedPrintOptionsActivity", 19},
- {"banner", 20},
- {"windowSwipeToDismiss", 20},
- {"isGame", 20},
- {"allowEmbedded", 20},
- {"setupActivity", 20},
- {"fastScrollStyle", 21},
- {"windowContentTransitions", 21},
- {"windowContentTransitionManager", 21},
- {"translationZ", 21},
- {"tintMode", 21},
- {"controlX1", 21},
- {"controlY1", 21},
- {"controlX2", 21},
- {"controlY2", 21},
- {"transitionName", 21},
- {"transitionGroup", 21},
- {"viewportWidth", 21},
- {"viewportHeight", 21},
- {"fillColor", 21},
- {"pathData", 21},
- {"strokeColor", 21},
- {"strokeWidth", 21},
- {"trimPathStart", 21},
- {"trimPathEnd", 21},
- {"trimPathOffset", 21},
- {"strokeLineCap", 21},
- {"strokeLineJoin", 21},
- {"strokeMiterLimit", 21},
- {"colorControlNormal", 21},
- {"colorControlActivated", 21},
- {"colorButtonNormal", 21},
- {"colorControlHighlight", 21},
- {"persistableMode", 21},
- {"titleTextAppearance", 21},
- {"subtitleTextAppearance", 21},
- {"slideEdge", 21},
- {"actionBarTheme", 21},
- {"textAppearanceListItemSecondary", 21},
- {"colorPrimary", 21},
- {"colorPrimaryDark", 21},
- {"colorAccent", 21},
- {"nestedScrollingEnabled", 21},
- {"windowEnterTransition", 21},
- {"windowExitTransition", 21},
- {"windowSharedElementEnterTransition", 21},
- {"windowSharedElementExitTransition", 21},
- {"windowAllowReturnTransitionOverlap", 21},
- {"windowAllowEnterTransitionOverlap", 21},
- {"sessionService", 21},
- {"stackViewStyle", 21},
- {"switchStyle", 21},
- {"elevation", 21},
- {"excludeId", 21},
- {"excludeClass", 21},
- {"hideOnContentScroll", 21},
- {"actionOverflowMenuStyle", 21},
- {"documentLaunchMode", 21},
- {"maxRecents", 21},
- {"autoRemoveFromRecents", 21},
- {"stateListAnimator", 21},
- {"toId", 21},
- {"fromId", 21},
- {"reversible", 21},
- {"splitTrack", 21},
- {"targetName", 21},
- {"excludeName", 21},
- {"matchOrder", 21},
- {"windowDrawsSystemBarBackgrounds", 21},
- {"statusBarColor", 21},
- {"navigationBarColor", 21},
- {"contentInsetStart", 21},
- {"contentInsetEnd", 21},
- {"contentInsetLeft", 21},
- {"contentInsetRight", 21},
- {"paddingMode", 21},
- {"layout_rowWeight", 21},
- {"layout_columnWeight", 21},
- {"translateX", 21},
- {"translateY", 21},
- {"selectableItemBackgroundBorderless", 21},
- {"elegantTextHeight", 21},
- {"searchKeyphraseId", 21},
- {"searchKeyphrase", 21},
- {"searchKeyphraseSupportedLocales", 21},
- {"windowTransitionBackgroundFadeDuration", 21},
- {"overlapAnchor", 21},
- {"progressTint", 21},
- {"progressTintMode", 21},
- {"progressBackgroundTint", 21},
- {"progressBackgroundTintMode", 21},
- {"secondaryProgressTint", 21},
- {"secondaryProgressTintMode", 21},
- {"indeterminateTint", 21},
- {"indeterminateTintMode", 21},
- {"backgroundTint", 21},
- {"backgroundTintMode", 21},
- {"foregroundTint", 21},
- {"foregroundTintMode", 21},
- {"buttonTint", 21},
- {"buttonTintMode", 21},
- {"thumbTint", 21},
- {"thumbTintMode", 21},
- {"fullBackupOnly", 21},
- {"propertyXName", 21},
- {"propertyYName", 21},
- {"relinquishTaskIdentity", 21},
- {"tileModeX", 21},
- {"tileModeY", 21},
- {"actionModeShareDrawable", 21},
- {"actionModeFindDrawable", 21},
- {"actionModeWebSearchDrawable", 21},
- {"transitionVisibilityMode", 21},
- {"minimumHorizontalAngle", 21},
- {"minimumVerticalAngle", 21},
- {"maximumAngle", 21},
- {"searchViewStyle", 21},
- {"closeIcon", 21},
- {"goIcon", 21},
- {"searchIcon", 21},
- {"voiceIcon", 21},
- {"commitIcon", 21},
- {"suggestionRowLayout", 21},
- {"queryBackground", 21},
- {"submitBackground", 21},
- {"buttonBarPositiveButtonStyle", 21},
- {"buttonBarNeutralButtonStyle", 21},
- {"buttonBarNegativeButtonStyle", 21},
- {"popupElevation", 21},
- {"actionBarPopupTheme", 21},
- {"multiArch", 21},
- {"touchscreenBlocksFocus", 21},
- {"windowElevation", 21},
- {"launchTaskBehindTargetAnimation", 21},
- {"launchTaskBehindSourceAnimation", 21},
- {"restrictionType", 21},
- {"dayOfWeekBackground", 21},
- {"dayOfWeekTextAppearance", 21},
- {"headerMonthTextAppearance", 21},
- {"headerDayOfMonthTextAppearance", 21},
- {"headerYearTextAppearance", 21},
- {"yearListItemTextAppearance", 21},
- {"yearListSelectorColor", 21},
- {"calendarTextColor", 21},
- {"recognitionService", 21},
- {"timePickerStyle", 21},
- {"timePickerDialogTheme", 21},
- {"headerTimeTextAppearance", 21},
- {"headerAmPmTextAppearance", 21},
- {"numbersTextColor", 21},
- {"numbersBackgroundColor", 21},
- {"numbersSelectorColor", 21},
- {"amPmTextColor", 21},
- {"amPmBackgroundColor", 21},
- {"searchKeyphraseRecognitionFlags", 21},
- {"checkMarkTint", 21},
- {"checkMarkTintMode", 21},
- {"popupTheme", 21},
- {"toolbarStyle", 21},
- {"windowClipToOutline", 21},
- {"datePickerDialogTheme", 21},
- {"showText", 21},
- {"windowReturnTransition", 21},
- {"windowReenterTransition", 21},
- {"windowSharedElementReturnTransition", 21},
- {"windowSharedElementReenterTransition", 21},
- {"resumeWhilePausing", 21},
- {"datePickerMode", 21},
- {"timePickerMode", 21},
- {"inset", 21},
- {"letterSpacing", 21},
- {"fontFeatureSettings", 21},
- {"outlineProvider", 21},
- {"contentAgeHint", 21},
- {"country", 21},
- {"windowSharedElementsUseOverlay", 21},
- {"reparent", 21},
- {"reparentWithOverlay", 21},
- {"ambientShadowAlpha", 21},
- {"spotShadowAlpha", 21},
- {"navigationIcon", 21},
- {"navigationContentDescription", 21},
- {"fragmentExitTransition", 21},
- {"fragmentEnterTransition", 21},
- {"fragmentSharedElementEnterTransition", 21},
- {"fragmentReturnTransition", 21},
- {"fragmentSharedElementReturnTransition", 21},
- {"fragmentReenterTransition", 21},
- {"fragmentAllowEnterTransitionOverlap", 21},
- {"fragmentAllowReturnTransitionOverlap", 21},
- {"patternPathData", 21},
- {"strokeAlpha", 21},
- {"fillAlpha", 21},
- {"windowActivityTransitions", 21},
- {"colorEdgeEffect", 21}};
-
-ApiVersion FindAttributeSdkLevel(const ResourceName& name) {
- if (name.package != "android" && name.type != ResourceType::kAttr) {
- return 0;
- }
-
- auto iter = sAttrMap.find(name.entry);
- if (iter != sAttrMap.end()) {
- return iter->second;
- }
- return SDK_LOLLIPOP_MR1;
-}
-
std::pair<StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion() {
return std::make_pair(StringPiece(sDevelopmentSdkCodeName), sDevelopmentSdkLevel);
}
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 5c32ed4fd849..5b7be3bb45ae 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -53,10 +53,10 @@ enum : ApiVersion {
SDK_NOUGAT_MR1 = 25,
SDK_O = 26,
SDK_O_MR1 = 27,
+ SDK_P = 10000, // STOPSHIP Replace with the real version.
};
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
-ApiVersion FindAttributeSdkLevel(const ResourceName& name);
std::pair<android::StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion();
} // namespace aapt
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index eb4fa494e53f..4e74ec366dab 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -22,12 +22,11 @@
namespace aapt {
-/**
- * Visits a value and invokes the appropriate method based on its type. Does not
- * traverse into compound types. Use ValueVisitor for that.
- */
-struct RawValueVisitor {
- virtual ~RawValueVisitor() = default;
+// Visits a value and invokes the appropriate method based on its type.
+// Does not traverse into compound types. Use ValueVisitor for that.
+class ValueVisitor {
+ public:
+ virtual ~ValueVisitor() = default;
virtual void VisitAny(Value* value) {}
virtual void VisitItem(Item* value) { VisitAny(value); }
@@ -46,21 +45,67 @@ struct RawValueVisitor {
virtual void Visit(Styleable* value) { VisitAny(value); }
};
+// Const version of ValueVisitor.
+class ConstValueVisitor {
+ public:
+ virtual ~ConstValueVisitor() = default;
+
+ virtual void VisitAny(const Value* value) {
+ }
+ virtual void VisitItem(const Item* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Reference* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const RawString* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const String* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const StyledString* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const FileReference* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const Id* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const BinaryPrimitive* value) {
+ VisitItem(value);
+ }
+
+ virtual void Visit(const Attribute* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Style* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Array* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Plural* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Styleable* value) {
+ VisitAny(value);
+ }
+};
+
// NOLINT, do not add parentheses around T.
#define DECL_VISIT_COMPOUND_VALUE(T) \
virtual void Visit(T* value) override { /* NOLINT */ \
VisitSubValues(value); \
}
-/**
- * Visits values, and if they are compound values, visits the components as
- * well.
- */
-struct ValueVisitor : public RawValueVisitor {
+// Visits values, and if they are compound values, descends into their components as well.
+struct DescendingValueVisitor : public ValueVisitor {
// The compiler will think we're hiding an overload, when we actually intend
// to call into RawValueVisitor. This will expose the visit methods in the
// super class so the compiler knows we are trying to call them.
- using RawValueVisitor::Visit;
+ using ValueVisitor::Visit;
void VisitSubValues(Attribute* attribute) {
for (Attribute::Symbol& symbol : attribute->symbols) {
@@ -106,37 +151,30 @@ struct ValueVisitor : public RawValueVisitor {
DECL_VISIT_COMPOUND_VALUE(Styleable);
};
-/**
- * Do not use directly. Helper struct for dyn_cast.
- */
+// Do not use directly. Helper struct for dyn_cast.
template <typename T>
-struct DynCastVisitor : public RawValueVisitor {
- T* value = nullptr;
+struct DynCastVisitor : public ConstValueVisitor {
+ const T* value = nullptr;
- void Visit(T* v) override { value = v; }
+ void Visit(const T* v) override {
+ value = v;
+ }
};
-/**
- * Specialization that checks if the value is an Item.
- */
+// Specialization that checks if the value is an Item.
template <>
-struct DynCastVisitor<Item> : public RawValueVisitor {
- Item* value = nullptr;
+struct DynCastVisitor<Item> : public ConstValueVisitor {
+ const Item* value = nullptr;
- void VisitItem(Item* item) override { value = item; }
+ void VisitItem(const Item* item) override {
+ value = item;
+ }
};
+// Returns a valid pointer to T if the value is an instance of T. Returns nullptr if value is
+// nullptr of if value is not an instance of T.
template <typename T>
const T* ValueCast(const Value* value) {
- return ValueCast<T>(const_cast<Value*>(value));
-}
-
-/**
- * Returns a valid pointer to T if the Value is of subtype T.
- * Otherwise, returns nullptr.
- */
-template <typename T>
-T* ValueCast(Value* value) {
if (!value) {
return nullptr;
}
@@ -145,8 +183,13 @@ T* ValueCast(Value* value) {
return visitor.value;
}
-inline void VisitAllValuesInPackage(ResourceTablePackage* pkg,
- RawValueVisitor* visitor) {
+// Non-const version of ValueCast.
+template <typename T>
+T* ValueCast(Value* value) {
+ return const_cast<T*>(ValueCast<T>(static_cast<const Value*>(value)));
+}
+
+inline void VisitAllValuesInPackage(ResourceTablePackage* pkg, ValueVisitor* visitor) {
for (auto& type : pkg->types) {
for (auto& entry : type->entries) {
for (auto& config_value : entry->values) {
@@ -156,8 +199,7 @@ inline void VisitAllValuesInPackage(ResourceTablePackage* pkg,
}
}
-inline void VisitAllValuesInTable(ResourceTable* table,
- RawValueVisitor* visitor) {
+inline void VisitAllValuesInTable(ResourceTable* table, ValueVisitor* visitor) {
for (auto& pkg : table->packages) {
VisitAllValuesInPackage(pkg.get(), visitor);
}
diff --git a/tools/aapt2/ValueVisitor_test.cpp b/tools/aapt2/ValueVisitor_test.cpp
index eb75b102e427..5aea77d67950 100644
--- a/tools/aapt2/ValueVisitor_test.cpp
+++ b/tools/aapt2/ValueVisitor_test.cpp
@@ -24,16 +24,16 @@
namespace aapt {
-struct SingleReferenceVisitor : public ValueVisitor {
- using ValueVisitor::Visit;
+struct SingleReferenceVisitor : public DescendingValueVisitor {
+ using DescendingValueVisitor::Visit;
Reference* visited = nullptr;
void Visit(Reference* ref) override { visited = ref; }
};
-struct StyleVisitor : public ValueVisitor {
- using ValueVisitor::Visit;
+struct StyleVisitor : public DescendingValueVisitor {
+ using DescendingValueVisitor::Visit;
std::list<Reference*> visited_refs;
Style* visited_style = nullptr;
@@ -42,7 +42,7 @@ struct StyleVisitor : public ValueVisitor {
void Visit(Style* style) override {
visited_style = style;
- ValueVisitor::Visit(style);
+ DescendingValueVisitor::Visit(style);
}
};
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 7f5bbf042766..a5e6aefd1e0f 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -35,12 +35,12 @@
#include "compile/Png.h"
#include "compile/PseudolocaleGenerator.h"
#include "compile/XmlIdCollector.h"
-#include "flatten/Archive.h"
-#include "flatten/XmlFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/XmlFlattener.h"
+#include "format/proto/ProtoSerialize.h"
#include "io/BigBufferOutputStream.h"
#include "io/FileInputStream.h"
#include "io/Util.h"
-#include "proto/ProtoSerialize.h"
#include "util/Files.h"
#include "util/Maybe.h"
#include "util/Util.h"
@@ -248,8 +248,9 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
// ZeroCopyOutputStream interface.
CopyingOutputStreamAdaptor copying_adaptor(writer);
- std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(&table);
- if (!pb_table->SerializeToZeroCopyStream(&copying_adaptor)) {
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(table, &pb_table);
+ if (!pb_table.SerializeToZeroCopyStream(&copying_adaptor)) {
context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write");
return false;
}
@@ -282,9 +283,10 @@ static bool WriteHeaderAndBufferToWriter(const StringPiece& output_path, const R
// Number of CompiledFiles.
output_stream.WriteLittleEndian32(1);
- std::unique_ptr<pb::internal::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
- output_stream.WriteCompiledFile(compiled_file.get());
- output_stream.WriteData(&buffer);
+ pb::internal::CompiledFile pb_compiled_file;
+ SerializeCompiledFileToPb(file, &pb_compiled_file);
+ output_stream.WriteCompiledFile(pb_compiled_file);
+ output_stream.WriteData(buffer);
if (output_stream.HadError()) {
diag->Error(DiagMessage(output_path) << "failed to write data");
@@ -319,8 +321,9 @@ static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, const Res
// Number of CompiledFiles.
output_stream.WriteLittleEndian32(1);
- std::unique_ptr<pb::internal::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
- output_stream.WriteCompiledFile(compiled_file.get());
+ pb::internal::CompiledFile pb_compiled_file;
+ SerializeCompiledFileToPb(file, &pb_compiled_file);
+ output_stream.WriteCompiledFile(pb_compiled_file);
output_stream.WriteData(map.getDataPtr(), map.getDataLength());
if (output_stream.HadError()) {
@@ -346,13 +349,13 @@ static bool FlattenXmlToOutStream(IAaptContext* context, const StringPiece& outp
return false;
}
- std::unique_ptr<pb::internal::CompiledFile> pb_compiled_file =
- SerializeCompiledFileToPb(xmlres->file);
- out->WriteCompiledFile(pb_compiled_file.get());
- out->WriteData(&buffer);
+ pb::internal::CompiledFile pb_compiled_file;
+ SerializeCompiledFileToPb(xmlres->file, &pb_compiled_file);
+ out->WriteCompiledFile(pb_compiled_file);
+ out->WriteData(buffer);
if (out->HadError()) {
- context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write data");
+ context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write XML data");
return false;
}
return true;
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 1a6f348a3edc..625c47cec939 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -22,7 +22,7 @@
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
@@ -325,9 +325,9 @@ static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, Loade
return diff;
}
-class ZeroingReferenceVisitor : public ValueVisitor {
+class ZeroingReferenceVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
void Visit(Reference* ref) override {
if (ref->name && ref->id) {
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 0965910ca853..44032f6a730d 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -21,10 +21,10 @@
#include "Debug.h"
#include "Diagnostics.h"
#include "Flags.h"
+#include "format/binary/BinaryResourceParser.h"
+#include "format/proto/ProtoDeserialize.h"
#include "io/ZipArchive.h"
#include "process/IResourceTableConsumer.h"
-#include "proto/ProtoSerialize.h"
-#include "unflatten/BinaryResourceParser.h"
#include "util/Files.h"
using ::android::StringPiece;
@@ -33,29 +33,28 @@ namespace aapt {
bool DumpCompiledFile(const pb::internal::CompiledFile& pb_file, const void* data, size_t len,
const Source& source, IAaptContext* context) {
- std::unique_ptr<ResourceFile> file =
- DeserializeCompiledFileFromPb(pb_file, source, context->GetDiagnostics());
- if (!file) {
- context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
+ ResourceFile file;
+ std::string error;
+ if (!DeserializeCompiledFileFromPb(pb_file, &file, &error)) {
+ context->GetDiagnostics()->Warn(DiagMessage(source)
+ << "failed to read compiled file: " << error);
return false;
}
- std::cout << "Resource: " << file->name << "\n"
- << "Config: " << file->config << "\n"
- << "Source: " << file->source << "\n";
+ std::cout << "Resource: " << file.name << "\n"
+ << "Config: " << file.config << "\n"
+ << "Source: " << file.source << "\n";
return true;
}
bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
- std::unique_ptr<ResourceTable> table;
-
std::string err;
std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
if (zip) {
- io::IFile* file = zip->FindFile("resources.arsc.flat");
- if (file) {
+ ResourceTable table;
+ if (io::IFile* file = zip->FindFile("resources.arsc.flat")) {
std::unique_ptr<io::IData> data = file->OpenAsData();
- if (!data) {
+ if (data == nullptr) {
context->GetDiagnostics()->Error(DiagMessage(file_path)
<< "failed to open resources.arsc.flat");
return false;
@@ -67,83 +66,78 @@ bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
return false;
}
- table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics());
- if (!table) {
+ ResourceTable table;
+ if (!DeserializeTableFromPb(pb_table, &table, &err)) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path)
+ << "failed to parse table: " << err);
+ return false;
+ }
+ } else if (io::IFile* file = zip->FindFile("resources.arsc")) {
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (!data) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.arsc");
return false;
}
- }
- if (!table) {
- file = zip->FindFile("resources.arsc");
- if (file) {
- std::unique_ptr<io::IData> data = file->OpenAsData();
- if (!data) {
- context->GetDiagnostics()->Error(DiagMessage(file_path)
- << "failed to open resources.arsc");
- return false;
- }
-
- table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(context, table.get(), Source(file_path), data->data(),
- data->size());
- if (!parser.Parse()) {
- return false;
- }
+ BinaryResourceParser parser(context, &table, Source(file_path), data->data(), data->size());
+ if (!parser.Parse()) {
+ return false;
}
}
+
+ DebugPrintTableOptions options;
+ options.show_sources = true;
+ Debug::PrintTable(&table, options);
+ return true;
}
- if (!table) {
- Maybe<android::FileMap> file = file::MmapPath(file_path, &err);
- if (!file) {
- context->GetDiagnostics()->Error(DiagMessage(file_path) << err);
- return false;
- }
+ err.clear();
- android::FileMap* file_map = &file.value();
+ Maybe<android::FileMap> file = file::MmapPath(file_path, &err);
+ if (!file) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path) << err);
+ return false;
+ }
- // Try as a compiled table.
- pb::ResourceTable pb_table;
- if (pb_table.ParseFromArray(file_map->getDataPtr(), file_map->getDataLength())) {
- table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics());
+ android::FileMap* file_map = &file.value();
+
+ // Check to see if this is a loose ResourceTable.
+ pb::ResourceTable pb_table;
+ if (pb_table.ParseFromArray(file_map->getDataPtr(), file_map->getDataLength())) {
+ ResourceTable table;
+ if (DeserializeTableFromPb(pb_table, &table, &err)) {
+ DebugPrintTableOptions options;
+ options.show_sources = true;
+ Debug::PrintTable(&table, options);
+ return true;
}
+ }
- if (!table) {
- // Try as a compiled file.
- CompiledFileInputStream input(file_map->getDataPtr(), file_map->getDataLength());
+ // Try as a compiled file.
+ CompiledFileInputStream input(file_map->getDataPtr(), file_map->getDataLength());
+ uint32_t num_files = 0;
+ if (!input.ReadLittleEndian32(&num_files)) {
+ return false;
+ }
- uint32_t num_files = 0;
- if (!input.ReadLittleEndian32(&num_files)) {
- return false;
- }
+ for (uint32_t i = 0; i < num_files; i++) {
+ pb::internal::CompiledFile compiled_file;
+ if (!input.ReadCompiledFile(&compiled_file)) {
+ context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
+ return false;
+ }
- for (uint32_t i = 0; i < num_files; i++) {
- pb::internal::CompiledFile compiled_file;
- if (!input.ReadCompiledFile(&compiled_file)) {
- context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
- return false;
- }
-
- uint64_t offset, len;
- if (!input.ReadDataMetaData(&offset, &len)) {
- context->GetDiagnostics()->Warn(DiagMessage() << "failed to read meta data");
- return false;
- }
-
- const void* data = static_cast<const uint8_t*>(file_map->getDataPtr()) + offset;
- if (!DumpCompiledFile(compiled_file, data, len, Source(file_path), context)) {
- return false;
- }
- }
+ uint64_t offset, len;
+ if (!input.ReadDataMetaData(&offset, &len)) {
+ context->GetDiagnostics()->Warn(DiagMessage() << "failed to read meta data");
+ return false;
}
- }
- if (table) {
- DebugPrintTableOptions options;
- options.show_sources = true;
- Debug::PrintTable(table.get(), options);
+ const void* data = static_cast<const uint8_t*>(file_map->getDataPtr()) + offset;
+ if (!DumpCompiledFile(compiled_file, data, len, Source(file_path), context)) {
+ return false;
+ }
}
-
return true;
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index d9e7ac6c1e08..40d71a3429d0 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -33,12 +33,17 @@
#include "Locale.h"
#include "NameMangler.h"
#include "ResourceUtils.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
#include "cmd/Util.h"
#include "compile/IdAssigner.h"
#include "filter/ConfigFilter.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
-#include "flatten/XmlFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/BinaryResourceParser.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
+#include "format/proto/ProtoDeserialize.h"
+#include "format/proto/ProtoSerialize.h"
#include "io/BigBufferInputStream.h"
#include "io/FileInputStream.h"
#include "io/FileSystem.h"
@@ -56,9 +61,7 @@
#include "optimize/VersionCollapser.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
-#include "proto/ProtoSerialize.h"
#include "split/TableSplitter.h"
-#include "unflatten/BinaryResourceParser.h"
#include "util/Files.h"
#include "xml/XmlDom.h"
@@ -251,10 +254,11 @@ class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
};
static bool FlattenXml(IAaptContext* context, xml::XmlResource* xml_res, const StringPiece& path,
- bool keep_raw_values, IArchiveWriter* writer) {
+ bool keep_raw_values, bool utf16, IArchiveWriter* writer) {
BigBuffer buffer(1024);
XmlFlattenerOptions options = {};
options.keep_raw_values = keep_raw_values;
+ options.use_utf16 = utf16;
XmlFlattener flattener(&buffer, options);
if (!flattener.Consume(context, xml_res)) {
return false;
@@ -279,8 +283,10 @@ static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source, cons
return {};
}
- std::unique_ptr<ResourceTable> table = DeserializeTableFromPb(pb_table, source, diag);
- if (!table) {
+ std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+ std::string error;
+ if (!DeserializeTableFromPb(pb_table, table.get(), &error)) {
+ diag->Error(DiagMessage(source) << "invalid compiled table: " << error);
return {};
}
return table;
@@ -441,7 +447,7 @@ static bool IsTransitionElement(const std::string& name) {
static bool IsVectorElement(const std::string& name) {
return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
- name == "objectAnimator";
+ name == "objectAnimator" || name == "gradient";
}
template <typename T>
@@ -457,7 +463,8 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
const Source& src = doc->file.source;
if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage() << "linking " << src.path);
+ context_->GetDiagnostics()->Note(DiagMessage()
+ << "linking " << src.path << " (" << doc->file.name << ")");
}
XmlReferenceLinker xml_linker;
@@ -505,6 +512,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
for (auto& pkg : table->packages) {
+ CHECK(!pkg->name.empty()) << "Packages must have names when being linked";
+
for (auto& type : pkg->types) {
// Sort by config and name, so that we get better locality in the zip file.
config_sorted_files.clear();
@@ -599,7 +608,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
}
}
error |= !FlattenXml(context_, doc.get(), dst_path, options_.keep_raw_values,
- archive_writer);
+ false /*utf16*/, archive_writer);
}
} else {
error |= !io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
@@ -701,7 +710,7 @@ class LinkCommand {
util::make_unique<AssetManagerSymbolSource>();
for (const std::string& path : options_.include_paths) {
if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage(path) << "loading include path");
+ context_->GetDiagnostics()->Note(DiagMessage() << "including " << path);
}
// First try to load the file as a static lib.
@@ -819,11 +828,9 @@ class LinkCommand {
return app_info;
}
- /**
- * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
- * Postcondition: ResourceTable has only one package left. All others are
- * stripped, or there is an error and false is returned.
- */
+ // Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
+ // Postcondition: ResourceTable has only one package left. All others are
+ // stripped, or there is an error and false is returned.
bool VerifyNoExternalPackages() {
auto is_ext_package_func = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
return context_->GetCompilationPackage() != pkg->name || !pkg->id ||
@@ -914,13 +921,14 @@ class LinkCommand {
}
bool FlattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
- std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table);
- return io::CopyProtoToArchive(context_, pb_table.get(), "resources.arsc.flat", 0, writer);
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(*table, &pb_table);
+ return io::CopyProtoToArchive(context_, &pb_table, "resources.arsc.flat", 0, writer);
}
bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate,
const StringPiece& out_package, const JavaClassGeneratorOptions& java_options,
- const Maybe<std::string> out_text_symbols_path = {}) {
+ const Maybe<std::string>& out_text_symbols_path = {}) {
if (!options_.generate_java_class_path) {
return true;
}
@@ -965,10 +973,94 @@ class LinkCommand {
context_->GetDiagnostics()->Error(DiagMessage()
<< "failed writing to '" << out_path
<< "': " << android::base::SystemErrorCodeToString(errno));
+ return false;
}
return true;
}
+ bool GenerateJavaClasses() {
+ // The set of packages whose R class to call in the main classes onResourcesLoaded callback.
+ std::vector<std::string> packages_to_callback;
+
+ JavaClassGeneratorOptions template_options;
+ template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ template_options.javadoc_annotations = options_.javadoc_annotations;
+
+ if (context_->GetPackageType() == PackageType::kStaticLib || options_.generate_non_final_ids) {
+ template_options.use_final = false;
+ }
+
+ if (context_->GetPackageType() == PackageType::kSharedLib) {
+ template_options.use_final = false;
+ template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
+ }
+
+ const StringPiece actual_package = context_->GetCompilationPackage();
+ StringPiece output_package = context_->GetCompilationPackage();
+ if (options_.custom_java_package) {
+ // Override the output java package to the custom one.
+ output_package = options_.custom_java_package.value();
+ }
+
+ // Generate the private symbols if required.
+ if (options_.private_symbols) {
+ packages_to_callback.push_back(options_.private_symbols.value());
+
+ // If we defined a private symbols package, we only emit Public symbols
+ // to the original package, and private and public symbols to the private package.
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
+ if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
+ options)) {
+ return false;
+ }
+ }
+
+ // Generate copies of the original package R class but with different package names.
+ // This is to support non-namespaced builds.
+ for (const std::string& extra_package : options_.extra_java_packages) {
+ packages_to_callback.push_back(extra_package);
+
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
+ return false;
+ }
+ }
+
+ // Generate R classes for each package that was merged (static library).
+ // Use the actual package's resources only.
+ for (const std::string& package : table_merger_->merged_packages()) {
+ packages_to_callback.push_back(package);
+
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ if (!WriteJavaFile(&final_table_, package, package, options)) {
+ return false;
+ }
+ }
+
+ // Generate the main public R class.
+ JavaClassGeneratorOptions options = template_options;
+
+ // Only generate public symbols if we have a private package.
+ if (options_.private_symbols) {
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+ }
+
+ if (options.rewrite_callback_options) {
+ options.rewrite_callback_options.value().packages_to_callback =
+ std::move(packages_to_callback);
+ }
+
+ if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
+ options_.generate_text_symbols_path)) {
+ return false;
+ }
+
+ return true;
+ }
+
bool WriteManifestJavaFile(xml::XmlResource* manifest_xml) {
if (!options_.generate_java_class_path) {
return true;
@@ -1097,15 +1189,17 @@ class LinkCommand {
bool result;
if (options_.no_static_lib_packages) {
- // Merge all resources as if they were in the compilation package. This is
- // the old behavior of aapt.
+ // Merge all resources as if they were in the compilation package. This is the old behavior
+ // of aapt.
- // Add the package to the set of --extra-packages so we emit an R.java for
- // each library package.
+ // Add the package to the set of --extra-packages so we emit an R.java for each library
+ // package.
if (!pkg->name.empty()) {
options_.extra_java_packages.insert(pkg->name);
}
+ // Clear the package name, so as to make the resources look like they are coming from the
+ // local package.
pkg->name = "";
if (override) {
result = table_merger_->MergeOverlay(Source(input), table.get(), collection.get());
@@ -1308,14 +1402,15 @@ class LinkCommand {
return false;
}
- std::unique_ptr<ResourceFile> resource_file = DeserializeCompiledFileFromPb(
- compiled_file, file->GetSource(), context_->GetDiagnostics());
- if (!resource_file) {
+ ResourceFile resource_file;
+ std::string error;
+ if (!DeserializeCompiledFileFromPb(compiled_file, &resource_file, &error)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "failed to read compiled header: " << error);
return false;
}
- if (!MergeCompiledFile(file->CreateFileSegment(offset, len), resource_file.get(),
- override)) {
+ if (!MergeCompiledFile(file->CreateFileSegment(offset, len), &resource_file, override)) {
return false;
}
}
@@ -1383,7 +1478,8 @@ class LinkCommand {
bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
ResourceTable* table) {
const bool keep_raw_values = context_->GetPackageType() == PackageType::kStaticLib;
- bool result = FlattenXml(context_, manifest, "AndroidManifest.xml", keep_raw_values, writer);
+ bool result = FlattenXml(context_, manifest, "AndroidManifest.xml", keep_raw_values,
+ true /*utf16*/, writer);
if (!result) {
return false;
}
@@ -1673,8 +1769,7 @@ class LinkCommand {
bool error = false;
{
- // AndroidManifest.xml has no resource name, but the CallSite is built
- // from the name
+ // AndroidManifest.xml has no resource name, but the CallSite is built from the name
// (aka, which package the AndroidManifest.xml is coming from).
// So we give it a package name so it can see local resources.
manifest_xml->file.name.package = context_->GetCompilationPackage();
@@ -1727,72 +1822,7 @@ class LinkCommand {
}
if (options_.generate_java_class_path) {
- // The set of packages whose R class to call in the main classes
- // onResourcesLoaded callback.
- std::vector<std::string> packages_to_callback;
-
- JavaClassGeneratorOptions template_options;
- template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- template_options.javadoc_annotations = options_.javadoc_annotations;
-
- if (context_->GetPackageType() == PackageType::kStaticLib ||
- options_.generate_non_final_ids) {
- template_options.use_final = false;
- }
-
- if (context_->GetPackageType() == PackageType::kSharedLib) {
- template_options.use_final = false;
- template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
- }
-
- const StringPiece actual_package = context_->GetCompilationPackage();
- StringPiece output_package = context_->GetCompilationPackage();
- if (options_.custom_java_package) {
- // Override the output java package to the custom one.
- output_package = options_.custom_java_package.value();
- }
-
- // Generate the private symbols if required.
- if (options_.private_symbols) {
- packages_to_callback.push_back(options_.private_symbols.value());
-
- // If we defined a private symbols package, we only emit Public symbols
- // to the original package, and private and public symbols to the
- // private package.
- JavaClassGeneratorOptions options = template_options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
- if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
- options)) {
- return 1;
- }
- }
-
- // Generate all the symbols for all extra packages.
- for (const std::string& extra_package : options_.extra_java_packages) {
- packages_to_callback.push_back(extra_package);
-
- JavaClassGeneratorOptions options = template_options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
- return 1;
- }
- }
-
- // Generate the main public R class.
- JavaClassGeneratorOptions options = template_options;
-
- // Only generate public symbols if we have a private package.
- if (options_.private_symbols) {
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
- }
-
- if (options.rewrite_callback_options) {
- options.rewrite_callback_options.value().packages_to_callback =
- std::move(packages_to_callback);
- }
-
- if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
- options_.generate_text_symbols_path)) {
+ if (!GenerateJavaClasses()) {
return 1;
}
}
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 9d71775889d4..67ac67a9367e 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -18,6 +18,8 @@
#include <vector>
#include "android-base/stringprintf.h"
+
+#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
#include "Diagnostics.h"
@@ -29,19 +31,23 @@
#include "cmd/Util.h"
#include "configuration/ConfigurationParser.h"
#include "filter/AbiFilter.h"
-#include "flatten/TableFlattener.h"
-#include "flatten/XmlFlattener.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
#include "io/BigBufferInputStream.h"
#include "io/Util.h"
+#include "optimize/MultiApkGenerator.h"
#include "optimize/ResourceDeduper.h"
#include "optimize/VersionCollapser.h"
#include "split/TableSplitter.h"
#include "util/Files.h"
+#include "util/Util.h"
using ::aapt::configuration::Abi;
using ::aapt::configuration::Artifact;
using ::aapt::configuration::PostProcessingConfiguration;
+using ::android::ResTable_config;
using ::android::StringPiece;
+using ::android::base::StringAppendF;
using ::android::base::StringPrintf;
namespace aapt {
@@ -188,42 +194,12 @@ class OptimizeCommand {
}
if (options_.configuration && options_.output_dir) {
- PostProcessingConfiguration& config = options_.configuration.value();
-
- // For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
- for (const Artifact& artifact : config.artifacts) {
- if (artifact.abi_group) {
- const std::string& group = artifact.abi_group.value();
-
- auto abi_group = config.abi_groups.find(group);
- // TODO: Remove validation when configuration parser ensures referential integrity.
- if (abi_group == config.abi_groups.end()) {
- context_->GetDiagnostics()->Note(
- DiagMessage() << "could not find referenced ABI group '" << group << "'");
- return 1;
- }
- FilterChain filters;
- filters.AddFilter(AbiFilter::FromAbiList(abi_group->second));
-
- const std::string& path = apk->GetSource().path;
- const StringPiece ext = file::GetExtension(path);
- const std::string name = path.substr(0, path.rfind(ext.to_string()));
-
- // Name is hard coded for now since only one split dimension is supported.
- // TODO: Incorporate name generation into the configuration objects.
- const std::string file_name =
- StringPrintf("%s.%s%s", name.c_str(), group.c_str(), ext.data());
- std::string out = options_.output_dir.value();
- file::AppendPath(&out, file_name);
-
- std::unique_ptr<IArchiveWriter> writer =
- CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);
-
- if (!apk->WriteToArchive(context_, options_.table_flattener_options, &filters,
- writer.get())) {
- return 1;
- }
- }
+ MultiApkGenerator generator{apk.get(), context_};
+ MultiApkGeneratorOptions generator_options = {options_.output_dir.value(),
+ options_.configuration.value(),
+ options_.table_flattener_options};
+ if (!generator.FromBaseApk(generator_options)) {
+ return 1;
}
}
@@ -260,7 +236,7 @@ class OptimizeCommand {
for (auto& entry : type->entries) {
for (auto& config_value : entry->values) {
- FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
+ auto* file_ref = ValueCast<FileReference>(config_value->value.get());
if (file_ref == nullptr) {
continue;
}
@@ -297,11 +273,8 @@ class OptimizeCommand {
}
io::BigBufferInputStream table_buffer_in(&table_buffer);
- if (!io::CopyInputStreamToArchive(context_, &table_buffer_in, "resources.arsc",
- ArchiveEntry::kAlign, writer)) {
- return false;
- }
- return true;
+ return io::CopyInputStreamToArchive(context_, &table_buffer_in, "resources.arsc",
+ ArchiveEntry::kAlign, writer);
}
OptimizeOptions options_;
@@ -310,24 +283,8 @@ class OptimizeCommand {
bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk,
OptimizeOptions* out_options) {
- io::IFile* manifest_file = apk->GetFileCollection()->FindFile("AndroidManifest.xml");
- if (manifest_file == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "missing AndroidManifest.xml");
- return false;
- }
-
- std::unique_ptr<io::IData> data = manifest_file->OpenAsData();
- if (data == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(manifest_file->GetSource())
- << "failed to open file");
- return false;
- }
-
- std::unique_ptr<xml::XmlResource> manifest = xml::Inflate(
- data->data(), data->size(), context->GetDiagnostics(), manifest_file->GetSource());
+ std::unique_ptr<xml::XmlResource> manifest = apk->InflateManifest(context);
if (manifest == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage() << "failed to read binary AndroidManifest.xml");
return false;
}
@@ -349,20 +306,29 @@ int Optimize(const std::vector<StringPiece>& args) {
OptimizeOptions options;
Maybe<std::string> config_path;
Maybe<std::string> target_densities;
+ Maybe<std::string> target_abis;
std::vector<std::string> configs;
std::vector<std::string> split_args;
bool verbose = false;
+ bool print_only = false;
Flags flags =
Flags()
.OptionalFlag("-o", "Path to the output APK.", &options.output_path)
.OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
.OptionalFlag("-x", "Path to XML configuration file.", &config_path)
+ .OptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only)
.OptionalFlag(
"--target-densities",
"Comma separated list of the screen densities that the APK will be optimized for.\n"
"All the resources that would be unused on devices of the given densities will be \n"
"removed from the APK.",
&target_densities)
+ .OptionalFlag(
+ "--target-abis",
+ "Comma separated list of the CPU ABIs that the APK will be optimized for.\n"
+ "All the native libraries that would be unused on devices of the given ABIs will \n"
+ "be removed from the APK.",
+ &target_abis)
.OptionalFlagList("-c",
"Comma separated list of configurations to include. The default\n"
"is all configurations.",
@@ -388,18 +354,19 @@ int Optimize(const std::vector<StringPiece>& args) {
return 1;
}
- std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[0]);
+ const std::string& apk_path = flags.GetArgs()[0];
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(&context, apk_path);
if (!apk) {
return 1;
}
context.SetVerbose(verbose);
+ IDiagnostics* diag = context.GetDiagnostics();
if (target_densities) {
// Parse the target screen densities.
for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) {
- Maybe<uint16_t> target_density =
- ParseTargetDensityParameter(config_str, context.GetDiagnostics());
+ Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
if (!target_density) {
return 1;
}
@@ -409,7 +376,7 @@ int Optimize(const std::vector<StringPiece>& args) {
std::unique_ptr<IConfigFilter> filter;
if (!configs.empty()) {
- filter = ParseConfigFilterParameters(configs, context.GetDiagnostics());
+ filter = ParseConfigFilterParameters(configs, diag);
if (filter == nullptr) {
return 1;
}
@@ -418,28 +385,47 @@ int Optimize(const std::vector<StringPiece>& args) {
// Parse the split parameters.
for (const std::string& split_arg : split_args) {
- options.split_paths.push_back({});
- options.split_constraints.push_back({});
- if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options.split_paths.back(),
+ options.split_paths.emplace_back();
+ options.split_constraints.emplace_back();
+ if (!ParseSplitParameter(split_arg, diag, &options.split_paths.back(),
&options.split_constraints.back())) {
return 1;
}
}
if (config_path) {
- if (!options.output_dir) {
- context.GetDiagnostics()->Error(
- DiagMessage() << "Output directory is required when using a configuration file");
- return 1;
- }
std::string& path = config_path.value();
Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
if (for_path) {
- options.configuration = for_path.value().WithDiagnostics(context.GetDiagnostics()).Parse();
+ options.configuration = for_path.value().WithDiagnostics(diag).Parse();
} else {
- context.GetDiagnostics()->Error(DiagMessage() << "Could not parse config file " << path);
+ diag->Error(DiagMessage() << "Could not parse config file " << path);
+ return 1;
+ }
+
+ if (print_only) {
+ std::vector<std::string> names;
+ const PostProcessingConfiguration& config = options.configuration.value();
+ if (!config.AllArtifactNames(file::GetFilename(apk_path), &names, diag)) {
+ diag->Error(DiagMessage() << "Failed to generate output artifact list");
+ return 1;
+ }
+
+ for (const auto& name : names) {
+ std::cout << name << std::endl;
+ }
+ return 0;
+ }
+
+ // Since we know that we are going to process the APK (not just print targets), make sure we
+ // have somewhere to write them to.
+ if (!options.output_dir) {
+ diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
return 1;
}
+ } else if (print_only) {
+ diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
+ return 1;
}
if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index 857cdd5365a0..a17926067a0b 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -69,10 +69,10 @@ class Visitor : public xml::PackageAwareVisitor {
// Use an empty string for the compilation package because we don't want to default to
// the local package if the user specified name="style" or something. This should just
// be the default namespace.
- Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package, {});
+ Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
if (!maybe_pkg) {
- context_->GetDiagnostics()->Error(DiagMessage(src) << "invalid namespace prefix '"
- << name.package << "'");
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "invalid namespace prefix '" << name.package << "'");
error_ = true;
return;
}
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
index 5af91fdc71b9..bc2e6990433c 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -21,8 +21,8 @@
#include "io/Io.h"
-using android::StringPiece;
-using android::base::StringPrintf;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 871ed4f01e77..36c24bc4a0fd 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -24,8 +24,8 @@
#include "compile/Pseudolocalizer.h"
#include "util/Util.h"
-using android::StringPiece;
-using android::StringPiece16;
+using ::android::StringPiece;
+using ::android::StringPiece16;
namespace aapt {
@@ -215,7 +215,7 @@ std::unique_ptr<StyledString> PseudolocalizeStyledString(StyledString* string,
namespace {
-class Visitor : public RawValueVisitor {
+class Visitor : public ValueVisitor {
public:
// Either value or item will be populated upon visiting the value.
std::unique_ptr<Value> value;
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index bdccf8bcae3a..a79a577c663b 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -27,9 +27,11 @@
#include "ConfigDescription.h"
#include "Diagnostics.h"
+#include "ResourceUtils.h"
#include "io/File.h"
#include "io/FileSystem.h"
#include "io/StringInputStream.h"
+#include "util/Files.h"
#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlActionExecutor.h"
@@ -58,6 +60,7 @@ using ::aapt::xml::XmlActionExecutor;
using ::aapt::xml::XmlActionExecutorPolicy;
using ::aapt::xml::XmlNodeAction;
using ::android::base::ReadFileToString;
+using ::android::StringPiece;
const std::unordered_map<std::string, Abi> kStringToAbiMap = {
{"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
@@ -116,56 +119,141 @@ const std::string& AbiToString(Abi abi) {
* success, or false if the either the placeholder is not found in the name, or the value is not
* present and the placeholder was.
*/
-static bool ReplacePlaceholder(const std::string& placeholder, const Maybe<std::string>& value,
+static bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
std::string* name, IDiagnostics* diag) {
- size_t offset = name->find(placeholder);
- if (value) {
- if (offset == std::string::npos) {
+ size_t offset = name->find(placeholder.data());
+ bool found = (offset != std::string::npos);
+
+ // Make sure the placeholder was present if the desired value is present.
+ if (!found) {
+ if (value) {
diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
return false;
}
- name->replace(offset, placeholder.length(), value.value());
return true;
}
+ DCHECK(found) << "Missing return path for placeholder not found";
+
// Make sure the placeholder was not present if the desired value was not present.
- bool result = (offset == std::string::npos);
- if (!result) {
+ if (!value) {
diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
+ return false;
+ }
+
+ name->replace(offset, placeholder.length(), value.value().data());
+
+ // Make sure there was only one instance of the placeholder.
+ if (name->find(placeholder.data()) != std::string::npos) {
+ diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Returns the common artifact base name from a template string.
+ */
+Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
+ const StringPiece ext = file::GetExtension(apk_name);
+ size_t end_index = apk_name.to_string().rfind(ext.to_string());
+ const std::string base_name =
+ (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
+
+ // Base name is optional.
+ if (result.find("${basename}") != std::string::npos) {
+ Maybe<StringPiece> maybe_base_name =
+ base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
+ if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
+ return {};
+ }
}
+
+ // Extension is optional.
+ if (result.find("${ext}") != std::string::npos) {
+ // Make sure we disregard the '.' in the extension when replacing the placeholder.
+ if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
+ return {};
+ }
+ } else {
+ // If no extension is specified, and the name template does not end in the current extension,
+ // add the existing extension.
+ if (!util::EndsWith(result, ext)) {
+ result.append(ext.to_string());
+ }
+ }
+
return result;
}
-Maybe<std::string> Artifact::ToArtifactName(const std::string& format, IDiagnostics* diag) const {
- std::string result = format;
+Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, const StringPiece& apk_name,
+ IDiagnostics* diag) const {
+ Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
+ if (!base) {
+ return {};
+ }
+ std::string result = std::move(base.value());
- if (!ReplacePlaceholder("{abi}", abi_group, &result, diag)) {
+ if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
return {};
}
- if (!ReplacePlaceholder("{density}", screen_density_group, &result, diag)) {
+ if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
return {};
}
- if (!ReplacePlaceholder("{locale}", locale_group, &result, diag)) {
+ if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
return {};
}
- if (!ReplacePlaceholder("{sdk}", android_sdk_group, &result, diag)) {
+ if (!ReplacePlaceholder("${sdk}", android_sdk_group, &result, diag)) {
return {};
}
- if (!ReplacePlaceholder("{feature}", device_feature_group, &result, diag)) {
+ if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
return {};
}
- if (!ReplacePlaceholder("{gl}", gl_texture_group, &result, diag)) {
+ if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
return {};
}
return result;
}
+Maybe<std::string> Artifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
+ if (!name) {
+ return {};
+ }
+
+ return ToBaseName(name.value(), apk_name, diag);
+}
+
+bool PostProcessingConfiguration::AllArtifactNames(const StringPiece& apk_name,
+ std::vector<std::string>* artifact_names,
+ IDiagnostics* diag) const {
+ for (const auto& artifact : artifacts) {
+ Maybe<std::string> name;
+ if (artifact.name) {
+ name = artifact.Name(apk_name, diag);
+ } else {
+ if (!artifact_format) {
+ diag->Error(DiagMessage() << "No global artifact template and an artifact name is missing");
+ return false;
+ }
+ name = artifact.ToArtifactName(artifact_format.value(), apk_name, diag);
+ }
+
+ if (!name) {
+ return false;
+ }
+
+ artifact_names->push_back(std::move(name.value()));
+ }
+
+ return true;
+}
+
} // namespace configuration
/** Returns a ConfigurationParser for the file located at the provided path. */
@@ -242,15 +330,32 @@ Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
// TODO: Validate all references in the configuration are valid. It should be safe to assume from
// this point on that any references from one section to another will be present.
+ // TODO: Automatically arrange artifacts so that they match Play Store multi-APK requirements.
+ // see: https://developer.android.com/google/play/publishing/multiple-apks.html
+ //
+ // For now, make sure the version codes are unique.
+ std::vector<Artifact>& artifacts = config.artifacts;
+ std::sort(artifacts.begin(), artifacts.end());
+ if (std::adjacent_find(artifacts.begin(), artifacts.end()) != artifacts.end()) {
+ diag_->Error(DiagMessage() << "Configuration has duplicate versions");
+ return {};
+ }
+
return {config};
}
ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
[](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ // This will be incremented later so the first version will always be different to the base APK.
+ int current_version = (config->artifacts.empty()) ? 0 : config->artifacts.back().version;
+
Artifact artifact{};
+ Maybe<int> version;
for (const auto& attr : root_element->attributes) {
if (attr.name == "name") {
artifact.name = attr.value;
+ } else if (attr.name == "version") {
+ version = std::stoi(attr.value);
} else if (attr.name == "abi-group") {
artifact.abi_group = {attr.value};
} else if (attr.name == "screen-density-group") {
@@ -268,6 +373,9 @@ ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
<< attr.value);
}
}
+
+ artifact.version = (version) ? version.value() : current_version + 1;
+
config->artifacts.push_back(artifact);
return true;
};
@@ -333,7 +441,10 @@ ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_han
if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
ConfigDescription config_descriptor;
const android::StringPiece& text = TrimWhitespace(t->text);
- if (ConfigDescription::Parse(text, &config_descriptor)) {
+ bool parsed = ConfigDescription::Parse(text, &config_descriptor);
+ if (parsed &&
+ (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
+ android::ResTable_config::CONFIG_DENSITY)) {
// Copy the density with the minimum SDK version stripped out.
group.push_back(config_descriptor.CopyWithoutSdkVersion());
} else {
@@ -366,17 +477,25 @@ ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ =
<< child->name);
valid = false;
} else {
- Locale entry;
- for (const auto& attr : child->attributes) {
- if (attr.name == "lang") {
- entry.lang = {attr.value};
- } else if (attr.name == "region") {
- entry.region = {attr.value};
- } else {
- diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
+ for (auto& node : child->children) {
+ xml::Text* t;
+ if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
+ ConfigDescription config_descriptor;
+ const android::StringPiece& text = TrimWhitespace(t->text);
+ bool parsed = ConfigDescription::Parse(text, &config_descriptor);
+ if (parsed &&
+ (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
+ android::ResTable_config::CONFIG_LOCALE)) {
+ // Copy the locale with the minimum SDK version stripped out.
+ group.push_back(config_descriptor.CopyWithoutSdkVersion());
+ } else {
+ diag->Error(DiagMessage()
+ << "Could not parse config descriptor for screen-density: " << text);
+ valid = false;
+ }
+ break;
}
}
- group.push_back(entry);
}
}
@@ -390,8 +509,8 @@ ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handle
return false;
}
- auto& group = config->android_sdk_groups[label];
bool valid = true;
+ bool found = false;
for (auto* child : root_element->GetChildElements()) {
if (child->name != "android-sdk") {
@@ -401,11 +520,11 @@ ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handle
AndroidSdk entry;
for (const auto& attr : child->attributes) {
if (attr.name == "minSdkVersion") {
- entry.min_sdk_version = {attr.value};
+ entry.min_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
} else if (attr.name == "targetSdkVersion") {
- entry.target_sdk_version = {attr.value};
+ entry.target_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
} else if (attr.name == "maxSdkVersion") {
- entry.max_sdk_version = {attr.value};
+ entry.max_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
} else {
diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
}
@@ -422,7 +541,11 @@ ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handle
}
}
- group.push_back(entry);
+ config->android_sdk_groups[label] = entry;
+ if (found) {
+ valid = false;
+ }
+ found = true;
}
}
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index 28c355e39643..c5d3284c33f4 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -17,6 +17,7 @@
#ifndef AAPT2_CONFIGURATION_H
#define AAPT2_CONFIGURATION_H
+#include <set>
#include <string>
#include <unordered_map>
#include <vector>
@@ -33,10 +34,20 @@ namespace configuration {
template<class T>
using Group = std::unordered_map<std::string, std::vector<T>>;
+/** A mapping of group label to a single configuration item. */
+template <class T>
+using Entry = std::unordered_map<std::string, T>;
+
/** Output artifact configuration options. */
struct Artifact {
/** Name to use for output of processing foo.apk -> foo.<name>.apk. */
- std::string name;
+ Maybe<std::string> name;
+ /**
+ * Value to add to the base Android manifest versionCode. If it is not present in the
+ * configuration file, it is set to the previous artifact + 1. If the first artifact does not have
+ * a value, artifacts are a 1 based index.
+ */
+ int version;
/** If present, uses the ABI group with this name. */
Maybe<std::string> abi_group;
/** If present, uses the screen density group with this name. */
@@ -51,7 +62,20 @@ struct Artifact {
Maybe<std::string> gl_texture_group;
/** Convert an artifact name template into a name string based on configuration contents. */
- Maybe<std::string> ToArtifactName(const std::string& format, IDiagnostics* diag) const;
+ Maybe<std::string> ToArtifactName(const android::StringPiece& format,
+ const android::StringPiece& apk_name, IDiagnostics* diag) const;
+
+ /** Convert an artifact name template into a name string based on configuration contents. */
+ Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
+
+ bool operator<(const Artifact& rhs) const {
+ // TODO(safarmer): Order by play store multi-APK requirements.
+ return version < rhs.version;
+ }
+
+ bool operator==(const Artifact& rhs) const {
+ return version == rhs.version;
+ }
};
/** Enumeration of currently supported ABIs. */
@@ -95,11 +119,17 @@ struct AndroidManifest {
};
struct AndroidSdk {
- Maybe<std::string> min_sdk_version;
- Maybe<std::string> target_sdk_version;
- Maybe<std::string> max_sdk_version;
+ Maybe<int> min_sdk_version;
+ Maybe<int> target_sdk_version;
+ Maybe<int> max_sdk_version;
Maybe<AndroidManifest> manifest;
+ static AndroidSdk ForMinSdk(int min_sdk) {
+ AndroidSdk sdk;
+ sdk.min_sdk_version = min_sdk;
+ return sdk;
+ }
+
inline friend bool operator==(const AndroidSdk& lhs, const AndroidSdk& rhs) {
return lhs.min_sdk_version == rhs.min_sdk_version &&
lhs.target_sdk_version == rhs.target_sdk_version &&
@@ -129,10 +159,14 @@ struct PostProcessingConfiguration {
Group<Abi> abi_groups;
Group<ConfigDescription> screen_density_groups;
- Group<Locale> locale_groups;
- Group<AndroidSdk> android_sdk_groups;
+ Group<ConfigDescription> locale_groups;
+ Entry<AndroidSdk> android_sdk_groups;
Group<DeviceFeature> device_feature_groups;
Group<GlTexture> gl_texture_groups;
+
+ /** Helper method that generates a list of artifact names and returns true on success. */
+ bool AllArtifactNames(const android::StringPiece& apk_name,
+ std::vector<std::string>* artifact_names, IDiagnostics* diag) const;
};
} // namespace configuration
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index f89773720cc5..3654901e3e02 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -24,6 +24,15 @@
#include "xml/XmlDom.h"
namespace aapt {
+
+namespace configuration {
+void PrintTo(const AndroidSdk& sdk, std::ostream* os) {
+ *os << "SDK: min=" << sdk.min_sdk_version.value_or_default(-1)
+ << ", target=" << sdk.target_sdk_version.value_or_default(-1)
+ << ", max=" << sdk.max_sdk_version.value_or_default(-1);
+}
+} // namespace configuration
+
namespace {
using ::android::ResTable_config;
@@ -64,20 +73,17 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
<screen-density>xxxhdpi</screen-density>
</screen-density-group>
<locale-group label="europe">
- <locale lang="en"/>
- <locale lang="es"/>
- <locale lang="fr"/>
- <locale lang="de"/>
+ <locale>en</locale>
+ <locale>es</locale>
+ <locale>fr</locale>
+ <locale>de</locale>
</locale-group>
<locale-group label="north-america">
- <locale lang="en"/>
- <locale lang="es" region="MX"/>
- <locale lang="fr" region="CA"/>
- </locale-group>
- <locale-group label="all">
- <locale/>
+ <locale>en</locale>
+ <locale>es-rMX</locale>
+ <locale>fr-rCA</locale>
</locale-group>
- <android-sdk-group label="19">
+ <android-sdk-group label="v19">
<android-sdk
minSdkVersion="19"
targetSdkVersion="24"
@@ -105,7 +111,7 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
abi-group="arm"
screen-density-group="large"
locale-group="europe"
- android-sdk-group="19"
+ android-sdk-group="v19"
gl-texture-group="dxt1"
device-feature-group="low-latency"/>
<artifact
@@ -113,7 +119,7 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
abi-group="other"
screen-density-group="alldpi"
locale-group="north-america"
- android-sdk-group="19"
+ android-sdk-group="v19"
gl-texture-group="dxt1"
device-feature-group="low-latency"/>
</artifacts>
@@ -153,13 +159,13 @@ TEST_F(ConfigurationParserTest, ValidateFile) {
EXPECT_EQ(3ul, value.screen_density_groups["large"].size());
EXPECT_EQ(6ul, value.screen_density_groups["alldpi"].size());
- EXPECT_EQ(3ul, value.locale_groups.size());
+ EXPECT_EQ(2ul, value.locale_groups.size());
EXPECT_EQ(4ul, value.locale_groups["europe"].size());
EXPECT_EQ(3ul, value.locale_groups["north-america"].size());
- EXPECT_EQ(1ul, value.locale_groups["all"].size());
EXPECT_EQ(1ul, value.android_sdk_groups.size());
- EXPECT_EQ(1ul, value.android_sdk_groups["19"].size());
+ EXPECT_TRUE(value.android_sdk_groups["v19"].min_sdk_version);
+ EXPECT_EQ(19, value.android_sdk_groups["v19"].min_sdk_version.value());
EXPECT_EQ(1ul, value.gl_texture_groups.size());
EXPECT_EQ(1ul, value.gl_texture_groups["dxt1"].size());
@@ -177,55 +183,117 @@ TEST_F(ConfigurationParserTest, InvalidNamespace) {
}
TEST_F(ConfigurationParserTest, ArtifactAction) {
- static constexpr const char* xml = R"xml(
+ PostProcessingConfiguration config;
+ {
+ const auto doc = test::BuildXmlDom(R"xml(
+ <artifact
+ abi-group="arm"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>)xml");
+
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_));
+
+ EXPECT_EQ(1ul, config.artifacts.size());
+
+ auto& artifact = config.artifacts.back();
+ EXPECT_FALSE(artifact.name); // TODO: make this fail.
+ EXPECT_EQ(1, artifact.version);
+ EXPECT_EQ("arm", artifact.abi_group.value());
+ EXPECT_EQ("large", artifact.screen_density_group.value());
+ EXPECT_EQ("europe", artifact.locale_group.value());
+ EXPECT_EQ("v19", artifact.android_sdk_group.value());
+ EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
+ EXPECT_EQ("low-latency", artifact.device_feature_group.value());
+ }
+
+ {
+ // Perform a second action to ensure we get 2 artifacts.
+ const auto doc = test::BuildXmlDom(R"xml(
+ <artifact
+ abi-group="other"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>)xml");
+
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
+ EXPECT_EQ(2ul, config.artifacts.size());
+ EXPECT_EQ(2, config.artifacts.back().version);
+ }
+
+ {
+ // Perform a third action with a set version code.
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact
- abi-group="arm"
+ version="5"
+ abi-group="other"
screen-density-group="large"
locale-group="europe"
- android-sdk-group="19"
+ android-sdk-group="v19"
gl-texture-group="dxt1"
- device-feature-group="low-latency"/>)xml";
+ device-feature-group="low-latency"/>)xml");
- auto doc = test::BuildXmlDom(xml);
-
- PostProcessingConfiguration config;
- bool ok = artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_);
- ASSERT_TRUE(ok);
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
+ EXPECT_EQ(3ul, config.artifacts.size());
+ EXPECT_EQ(5, config.artifacts.back().version);
+ }
- EXPECT_EQ(1ul, config.artifacts.size());
-
- auto& artifact = config.artifacts.front();
- EXPECT_EQ("", artifact.name); // TODO: make this fail.
- EXPECT_EQ("arm", artifact.abi_group.value());
- EXPECT_EQ("large", artifact.screen_density_group.value());
- EXPECT_EQ("europe", artifact.locale_group.value());
- EXPECT_EQ("19", artifact.android_sdk_group.value());
- EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
- EXPECT_EQ("low-latency", artifact.device_feature_group.value());
-
- // Perform a second action to ensure we get 2 artifacts.
- static constexpr const char* second = R"xml(
+ {
+ // Perform a fourth action to ensure the version code still increments.
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact
abi-group="other"
screen-density-group="large"
locale-group="europe"
- android-sdk-group="19"
+ android-sdk-group="v19"
gl-texture-group="dxt1"
- device-feature-group="low-latency"/>)xml";
- doc = test::BuildXmlDom(second);
+ device-feature-group="low-latency"/>)xml");
- ok = artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
- ASSERT_TRUE(ok);
- EXPECT_EQ(2ul, config.artifacts.size());
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
+ EXPECT_EQ(4ul, config.artifacts.size());
+ EXPECT_EQ(6, config.artifacts.back().version);
+ }
+}
+
+TEST_F(ConfigurationParserTest, DuplicateArtifactVersion) {
+ static constexpr const char* configuration = R"xml(<?xml version="1.0" encoding="utf-8" ?>
+ <pst-process xmlns="http://schemas.android.com/tools/aapt">>
+ <artifacts>
+ <artifact-format>
+ ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
+ </artifact-format>
+ <artifact
+ name="art1"
+ abi-group="arm"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>
+ <artifact
+ name="art2"
+ version = "1"
+ abi-group="other"
+ screen-density-group="alldpi"
+ locale-group="north-america"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>
+ </artifacts>
+ </post-process>)xml";
+ auto result = ConfigurationParser::ForContents(configuration).Parse();
+ ASSERT_FALSE(result);
}
TEST_F(ConfigurationParserTest, ArtifactFormatAction) {
- static constexpr const char* xml = R"xml(
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact-format>
${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
- </artifact-format>)xml";
-
- auto doc = test::BuildXmlDom(xml);
+ </artifact-format>)xml");
PostProcessingConfiguration config;
bool ok = artifact_format_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
@@ -295,10 +363,10 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
TEST_F(ConfigurationParserTest, LocaleGroupAction) {
static constexpr const char* xml = R"xml(
<locale-group label="europe">
- <locale lang="en"/>
- <locale lang="es"/>
- <locale lang="fr"/>
- <locale lang="de"/>
+ <locale>en</locale>
+ <locale>es</locale>
+ <locale>fr</locale>
+ <locale>de</locale>
</locale-group>)xml";
auto doc = test::BuildXmlDom(xml);
@@ -310,23 +378,19 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction) {
ASSERT_EQ(1ul, config.locale_groups.size());
ASSERT_EQ(1u, config.locale_groups.count("europe"));
- auto& out = config.locale_groups["europe"];
+ const auto& out = config.locale_groups["europe"];
- Locale en;
- en.lang = std::string("en");
- Locale es;
- es.lang = std::string("es");
- Locale fr;
- fr.lang = std::string("fr");
- Locale de;
- de.lang = std::string("de");
+ ConfigDescription en = test::ParseConfigOrDie("en");
+ ConfigDescription es = test::ParseConfigOrDie("es");
+ ConfigDescription fr = test::ParseConfigOrDie("fr");
+ ConfigDescription de = test::ParseConfigOrDie("de");
ASSERT_THAT(out, ElementsAre(en, es, fr, de));
}
TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) {
static constexpr const char* xml = R"xml(
- <android-sdk-group label="19">
+ <android-sdk-group label="v19">
<android-sdk
minSdkVersion="19"
targetSdkVersion="24"
@@ -344,18 +408,46 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) {
ASSERT_TRUE(ok);
ASSERT_EQ(1ul, config.android_sdk_groups.size());
- ASSERT_EQ(1u, config.android_sdk_groups.count("19"));
+ ASSERT_EQ(1u, config.android_sdk_groups.count("v19"));
- auto& out = config.android_sdk_groups["19"];
+ auto& out = config.android_sdk_groups["v19"];
AndroidSdk sdk;
- sdk.min_sdk_version = std::string("19");
- sdk.target_sdk_version = std::string("24");
- sdk.max_sdk_version = std::string("25");
+ sdk.min_sdk_version = 19;
+ sdk.target_sdk_version = 24;
+ sdk.max_sdk_version = 25;
sdk.manifest = AndroidManifest();
- ASSERT_EQ(1ul, out.size());
- ASSERT_EQ(sdk, out[0]);
+ ASSERT_EQ(sdk, out);
+}
+
+TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
+ static constexpr const char* xml = R"xml(
+ <android-sdk-group label="O">
+ <android-sdk
+ minSdkVersion="M"
+ targetSdkVersion="O"
+ maxSdkVersion="O">
+ </android-sdk>
+ </android-sdk-group>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = android_sdk_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+
+ ASSERT_EQ(1ul, config.android_sdk_groups.size());
+ ASSERT_EQ(1u, config.android_sdk_groups.count("O"));
+
+ auto& out = config.android_sdk_groups["O"];
+
+ AndroidSdk sdk;
+ sdk.min_sdk_version = {}; // Only the latest development version is supported.
+ sdk.target_sdk_version = 26;
+ sdk.max_sdk_version = 26;
+
+ ASSERT_EQ(sdk, out);
}
TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
@@ -415,21 +507,43 @@ TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) {
ASSERT_THAT(out, ElementsAre(low_latency, pro));
}
+// Artifact name parser test cases.
+
TEST(ArtifactTest, Simple) {
StdErrDiagnostics diag;
Artifact x86;
x86.abi_group = {"x86"};
- auto x86_result = x86.ToArtifactName("something.{abi}.apk", &diag);
+ auto x86_result = x86.ToArtifactName("something.${abi}.apk", "", &diag);
ASSERT_TRUE(x86_result);
EXPECT_EQ(x86_result.value(), "something.x86.apk");
Artifact arm;
arm.abi_group = {"armeabi-v7a"};
- auto arm_result = arm.ToArtifactName("app.{abi}.apk", &diag);
- ASSERT_TRUE(arm_result);
- EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ {
+ auto arm_result = arm.ToArtifactName("app.${abi}.apk", "", &diag);
+ ASSERT_TRUE(arm_result);
+ EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ }
+
+ {
+ auto arm_result = arm.ToArtifactName("app.${abi}.apk", "different_name.apk", &diag);
+ ASSERT_TRUE(arm_result);
+ EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ }
+
+ {
+ auto arm_result = arm.ToArtifactName("${basename}.${abi}.apk", "app.apk", &diag);
+ ASSERT_TRUE(arm_result);
+ EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ }
+
+ {
+ auto arm_result = arm.ToArtifactName("app.${abi}.${ext}", "app.apk", &diag);
+ ASSERT_TRUE(arm_result);
+ EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ }
}
TEST(ArtifactTest, Complex) {
@@ -440,12 +554,42 @@ TEST(ArtifactTest, Complex) {
artifact.device_feature_group = {"df1"};
artifact.gl_texture_group = {"glx1"};
artifact.locale_group = {"en-AU"};
- artifact.android_sdk_group = {"26"};
-
- auto result =
- artifact.ToArtifactName("app.{density}_{locale}_{feature}_{gl}.sdk{sdk}.{abi}.apk", &diag);
- ASSERT_TRUE(result);
- EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+ artifact.android_sdk_group = {"v26"};
+
+ {
+ auto result = artifact.ToArtifactName(
+ "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
+ }
+
+ {
+ auto result = artifact.ToArtifactName(
+ "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "app.apk", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
+ }
+
+ {
+ auto result = artifact.ToArtifactName(
+ "${basename}.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "app.apk", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
+ }
+
+ {
+ auto result = artifact.ToArtifactName(
+ "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.${ext}", "app.apk", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
+ }
+
+ {
+ auto result = artifact.ToArtifactName(
+ "${basename}.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}", "app.apk", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
+ }
}
TEST(ArtifactTest, Missing) {
@@ -453,16 +597,69 @@ TEST(ArtifactTest, Missing) {
Artifact x86;
x86.abi_group = {"x86"};
- EXPECT_FALSE(x86.ToArtifactName("something.{density}.apk", &diag));
- EXPECT_FALSE(x86.ToArtifactName("something.apk", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.apk", "", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "something.apk", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.apk", "something.apk", &diag));
}
TEST(ArtifactTest, Empty) {
StdErrDiagnostics diag;
Artifact artifact;
- EXPECT_FALSE(artifact.ToArtifactName("something.{density}.apk", &diag));
- EXPECT_TRUE(artifact.ToArtifactName("something.apk", &diag));
+ EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
+ EXPECT_TRUE(artifact.ToArtifactName("something.apk", "", &diag));
+ EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag));
+ EXPECT_TRUE(artifact.ToArtifactName("something.apk", "something.apk", &diag));
+}
+
+TEST(ArtifactTest, Repeated) {
+ StdErrDiagnostics diag;
+ Artifact artifact;
+ artifact.screen_density_group = {"mdpi"};
+
+ ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
+ EXPECT_FALSE(artifact.ToArtifactName("something.${density}.${density}.apk", "", &diag));
+ ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag));
+}
+
+TEST(ArtifactTest, Nesting) {
+ StdErrDiagnostics diag;
+ Artifact x86;
+ x86.abi_group = {"x86"};
+
+ EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", "", &diag));
+
+ const Maybe<std::string>& name = x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
+ ASSERT_TRUE(name);
+ EXPECT_EQ(name.value(), "something.${abix86}.apk");
+}
+
+TEST(ArtifactTest, Recursive) {
+ StdErrDiagnostics diag;
+ Artifact artifact;
+ artifact.device_feature_group = {"${gl}"};
+ artifact.gl_texture_group = {"glx1"};
+
+ EXPECT_FALSE(artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag));
+
+ artifact.device_feature_group = {"df1"};
+ artifact.gl_texture_group = {"${feature}"};
+ {
+ const auto& result = artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.df1.${feature}.apk");
+ }
+
+ // This is an invalid case, but should be the only possible case due to the ordering of
+ // replacement.
+ artifact.device_feature_group = {"${gl}"};
+ artifact.gl_texture_group = {"glx1"};
+ {
+ const auto& result = artifact.ToArtifactName("app.${feature}.apk", "", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.glx1.apk");
+ }
}
} // namespace
diff --git a/tools/aapt2/configuration/aapt2.xsd b/tools/aapt2/configuration/aapt2.xsd
index 47bf99e28089..134153a017f8 100644
--- a/tools/aapt2/configuration/aapt2.xsd
+++ b/tools/aapt2/configuration/aapt2.xsd
@@ -39,6 +39,7 @@
<!-- Groups output artifacts together by dimension labels. -->
<xsd:complexType name="artifact">
<xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="version" type="xsd:integer"/>
<xsd:attribute name="abi-group" type="xsd:string"/>
<xsd:attribute name="android-sdk-group" type="xsd:string"/>
<xsd:attribute name="device-feature-group" type="xsd:string"/>
diff --git a/tools/aapt2/filter/ConfigFilter.h b/tools/aapt2/filter/ConfigFilter.h
index 3f1341684912..ebb81519dceb 100644
--- a/tools/aapt2/filter/ConfigFilter.h
+++ b/tools/aapt2/filter/ConfigFilter.h
@@ -38,13 +38,9 @@ class IConfigFilter {
};
/**
- * Implements config axis matching. An axis is one component of a configuration,
- * like screen
- * density or locale. If an axis is specified in the filter, and the axis is
- * specified in
- * the configuration to match, they must be compatible. Otherwise the
- * configuration to match is
- * accepted.
+ * Implements config axis matching. An axis is one component of a configuration, like screen density
+ * or locale. If an axis is specified in the filter, and the axis is specified in the configuration
+ * to match, they must be compatible. Otherwise the configuration to match is accepted.
*
* Used when handling "-c" options.
*/
diff --git a/tools/aapt2/filter/Filter_test.cpp b/tools/aapt2/filter/Filter_test.cpp
index fb75a4b4d7c1..db2e69fc90d3 100644
--- a/tools/aapt2/filter/Filter_test.cpp
+++ b/tools/aapt2/filter/Filter_test.cpp
@@ -25,22 +25,16 @@
namespace aapt {
namespace {
-TEST(FilterChainTest, EmptyChain) {
+TEST(FilterTest, FilterChain) {
FilterChain chain;
ASSERT_TRUE(chain.Keep("some/random/path"));
-}
-TEST(FilterChainTest, SingleFilter) {
- FilterChain chain;
chain.AddFilter(util::make_unique<PrefixFilter>("keep/"));
ASSERT_FALSE(chain.Keep("removed/path"));
ASSERT_TRUE(chain.Keep("keep/path/1"));
ASSERT_TRUE(chain.Keep("keep/path/2"));
-}
-TEST(FilterChainTest, MultipleFilters) {
- FilterChain chain;
chain.AddFilter(util::make_unique<PrefixFilter>("keep/"));
chain.AddFilter(util::make_unique<PrefixFilter>("keep/really/"));
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/format/Archive.cpp
index 5f8bd063f9b0..d152a9cc7e62 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/format/Archive.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "flatten/Archive.h"
+#include "format/Archive.h"
#include <cstdio>
#include <memory>
@@ -106,9 +106,13 @@ class DirectoryWriter : public IArchiveWriter {
return !in->HadError();
}
- bool HadError() const override { return !error_.empty(); }
+ bool HadError() const override {
+ return !error_.empty();
+ }
- std::string GetError() const override { return error_; }
+ std::string GetError() const override {
+ return error_;
+ }
private:
DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
@@ -221,9 +225,13 @@ class ZipFileWriter : public IArchiveWriter {
}
}
- bool HadError() const override { return !error_.empty(); }
+ bool HadError() const override {
+ return !error_.empty();
+ }
- std::string GetError() const override { return error_; }
+ std::string GetError() const override {
+ return error_;
+ }
virtual ~ZipFileWriter() {
if (writer_) {
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/format/Archive.h
index 4ee4ce71a5c5..4e8a39df9165 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/format/Archive.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_FLATTEN_ARCHIVE_H
-#define AAPT_FLATTEN_ARCHIVE_H
+#ifndef AAPT_FORMAT_ARCHIVE_H
+#define AAPT_FORMAT_ARCHIVE_H
#include <fstream>
#include <memory>
@@ -78,4 +78,4 @@ std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(IDiagnostics* diag,
} // namespace aapt
-#endif /* AAPT_FLATTEN_ARCHIVE_H */
+#endif /* AAPT_FORMAT_ARCHIVE_H */
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 892aee6fadcb..95eec4ae8248 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "unflatten/BinaryResourceParser.h"
+#include "format/binary/BinaryResourceParser.h"
#include <algorithm>
#include <map>
@@ -31,22 +31,22 @@
#include "ResourceValues.h"
#include "Source.h"
#include "ValueVisitor.h"
-#include "unflatten/ResChunkPullParser.h"
+#include "format/binary/ResChunkPullParser.h"
#include "util/Util.h"
-namespace aapt {
-
using namespace android;
using ::android::base::StringPrintf;
+namespace aapt {
+
namespace {
// Visitor that converts a reference's resource ID to a resource name, given a mapping from
// resource ID to resource name.
-class ReferenceIdToNameVisitor : public ValueVisitor {
+class ReferenceIdToNameVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping)
: mapping_(mapping) {
@@ -118,15 +118,11 @@ bool BinaryResourceParser::Parse() {
return true;
}
-/**
- * Parses the resource table, which contains all the packages, types, and
- * entries.
- */
+// Parses the resource table, which contains all the packages, types, and entries.
bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
const ResTable_header* table_header = ConvertTo<ResTable_header>(chunk);
if (!table_header) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "corrupt ResTable_header chunk");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_header chunk");
return false;
}
@@ -136,21 +132,20 @@ bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
switch (util::DeviceToHost16(parser.chunk()->type)) {
case android::RES_STRING_POOL_TYPE:
if (value_pool_.getError() == NO_INIT) {
- status_t err = value_pool_.setTo(
- parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+ status_t err =
+ value_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
if (err != NO_ERROR) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "corrupt string pool in ResTable: "
- << value_pool_.getError());
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt string pool in ResTable: "
+ << value_pool_.getError());
return false;
}
// Reserve some space for the strings we are going to add.
- table_->string_pool.HintWillAdd(value_pool_.size(),
- value_pool_.styleCount());
+ table_->string_pool.HintWillAdd(value_pool_.size(), value_pool_.styleCount());
} else {
- context_->GetDiagnostics()->Warn(
- DiagMessage(source_) << "unexpected string pool in ResTable");
+ context_->GetDiagnostics()->Warn(DiagMessage(source_)
+ << "unexpected string pool in ResTable");
}
break;
@@ -169,8 +164,8 @@ bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
}
if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "corrupt resource table: " << parser.error());
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt resource table: " << parser.error());
return false;
}
return true;
@@ -187,26 +182,25 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
uint32_t package_id = util::DeviceToHost32(package_header->id);
if (package_id > std::numeric_limits<uint8_t>::max()) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "package ID is too big (" << package_id << ")");
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "package ID is too big (" << package_id << ")");
return false;
}
// Extract the package name.
- size_t len = strnlen16((const char16_t*)package_header->name,
- arraysize(package_header->name));
+ size_t len = strnlen16((const char16_t*)package_header->name, arraysize(package_header->name));
std::u16string package_name;
package_name.resize(len);
for (size_t i = 0; i < len; i++) {
package_name[i] = util::DeviceToHost16(package_header->name[i]);
}
- ResourceTablePackage* package = table_->CreatePackage(
- util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
+ ResourceTablePackage* package =
+ table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
if (!package) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "incompatible package '" << package_name
- << "' with ID " << package_id);
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "incompatible package '" << package_name << "' with ID "
+ << package_id);
return false;
}
@@ -221,23 +215,21 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
switch (util::DeviceToHost16(parser.chunk()->type)) {
case android::RES_STRING_POOL_TYPE:
if (type_pool_.getError() == NO_INIT) {
- status_t err = type_pool_.setTo(
- parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+ status_t err =
+ type_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
if (err != NO_ERROR) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
<< "corrupt type string pool in "
- << "ResTable_package: "
- << type_pool_.getError());
+ << "ResTable_package: " << type_pool_.getError());
return false;
}
} else if (key_pool_.getError() == NO_INIT) {
- status_t err = key_pool_.setTo(
- parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+ status_t err =
+ key_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
if (err != NO_ERROR) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
<< "corrupt key string pool in "
- << "ResTable_package: "
- << key_pool_.getError());
+ << "ResTable_package: " << key_pool_.getError());
return false;
}
} else {
@@ -272,8 +264,8 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
}
if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "corrupt ResTable_package: " << parser.error());
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt ResTable_package: " << parser.error());
return false;
}
@@ -286,22 +278,19 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) {
if (type_pool_.getError() != NO_ERROR) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "missing type string pool");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool");
return false;
}
const ResTable_typeSpec* type_spec = ConvertTo<ResTable_typeSpec>(chunk);
if (!type_spec) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "corrupt ResTable_typeSpec chunk");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_typeSpec chunk");
return false;
}
if (type_spec->id == 0) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "ResTable_typeSpec has invalid id: "
- << type_spec->id);
+ << "ResTable_typeSpec has invalid id: " << type_spec->id);
return false;
}
return true;
@@ -310,14 +299,12 @@ bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) {
bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
const ResChunk_header* chunk) {
if (type_pool_.getError() != NO_ERROR) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "missing type string pool");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool");
return false;
}
if (key_pool_.getError() != NO_ERROR) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "missing key string pool");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing key string pool");
return false;
}
@@ -325,15 +312,13 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
// a lot and has its own code to handle variable size.
const ResTable_type* type = ConvertTo<ResTable_type, kResTableTypeMinSize>(chunk);
if (!type) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "corrupt ResTable_type chunk");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_type chunk");
return false;
}
if (type->id == 0) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "ResTable_type has invalid id: "
- << (int)type->id);
+ << "ResTable_type has invalid id: " << (int)type->id);
return false;
}
@@ -344,9 +329,9 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
const ResourceType* parsed_type = ParseResourceType(type_str);
if (!parsed_type) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "invalid type name '" << type_str
- << "' for type with ID " << (int)type->id);
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "invalid type name '" << type_str << "' for type with ID "
+ << (int)type->id);
return false;
}
@@ -357,12 +342,10 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
continue;
}
- const ResourceName name(
- package->name, *parsed_type,
- util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)));
+ const ResourceName name(package->name, *parsed_type,
+ util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)));
- const ResourceId res_id(package->id.value(), type->id,
- static_cast<uint16_t>(it.index()));
+ const ResourceId res_id(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
std::unique_ptr<Value> resource_value;
if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
@@ -377,10 +360,9 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
}
if (!resource_value) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "failed to parse value for resource " << name
- << " (" << res_id << ") with configuration '"
- << config << "'");
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to parse value for resource " << name << " ("
+ << res_id << ") with configuration '" << config << "'");
return false;
}
@@ -433,19 +415,19 @@ std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& na
if (file_ref != nullptr) {
file_ref->file = files_->FindFile(*file_ref->path);
if (file_ref->file == nullptr) {
- context_->GetDiagnostics()->Warn(DiagMessage() << "resource " << name << " for config '"
- << config << "' is a file reference to '"
- << *file_ref->path
- << "' but no such path exists");
+ context_->GetDiagnostics()->Warn(DiagMessage()
+ << "resource " << name << " for config '" << config
+ << "' is a file reference to '" << *file_ref->path
+ << "' but no such path exists");
}
}
}
return item;
}
-std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
+std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
switch (name.type) {
case ResourceType::kStyle:
return ParseStyle(name, config, map);
@@ -470,9 +452,9 @@ std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(
return {};
}
-std::unique_ptr<Style> BinaryResourceParser::ParseStyle(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
+std::unique_ptr<Style> BinaryResourceParser::ParseStyle(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
std::unique_ptr<Style> style = util::make_unique<Style>();
if (util::DeviceToHost32(map->parent.ident) != 0) {
// The parent is a regular reference to a resource.
@@ -495,19 +477,16 @@ std::unique_ptr<Style> BinaryResourceParser::ParseStyle(
return style;
}
-std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
- const bool is_weak =
- (util::DeviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
+std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ const bool is_weak = (util::DeviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(is_weak);
// First we must discover what type of attribute this is. Find the type mask.
- auto type_mask_iter =
- std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
- return util::DeviceToHost32(entry.name.ident) ==
- ResTable_map::ATTR_TYPE;
- });
+ auto type_mask_iter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
+ return util::DeviceToHost32(entry.name.ident) == ResTable_map::ATTR_TYPE;
+ });
if (type_mask_iter != end(map)) {
attr->type_mask = util::DeviceToHost32(type_mask_iter->value.data);
@@ -526,8 +505,7 @@ std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(
continue;
}
- if (attr->type_mask &
- (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
+ if (attr->type_mask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
Attribute::Symbol symbol;
symbol.value = util::DeviceToHost32(map_entry.value.data);
symbol.symbol = Reference(util::DeviceToHost32(map_entry.name.ident));
@@ -539,9 +517,9 @@ std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(
return attr;
}
-std::unique_ptr<Array> BinaryResourceParser::ParseArray(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
+std::unique_ptr<Array> BinaryResourceParser::ParseArray(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
std::unique_ptr<Array> array = util::make_unique<Array>();
for (const ResTable_map& map_entry : map) {
array->elements.push_back(ParseValue(name, config, map_entry.value));
@@ -549,9 +527,9 @@ std::unique_ptr<Array> BinaryResourceParser::ParseArray(
return array;
}
-std::unique_ptr<Plural> BinaryResourceParser::ParsePlural(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
+std::unique_ptr<Plural> BinaryResourceParser::ParsePlural(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
for (const ResTable_map& map_entry : map) {
std::unique_ptr<Item> item = ParseValue(name, config, map_entry.value);
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index c41ada09e551..dc9a3847a9c6 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_BINARY_RESOURCE_PARSER_H
-#define AAPT_BINARY_RESOURCE_PARSER_H
+#ifndef AAPT_FORMAT_BINARY_RESOURCEPARSER_H
+#define AAPT_FORMAT_BINARY_RESOURCEPARSER_H
#include <string>
@@ -32,25 +32,17 @@ namespace aapt {
struct SymbolTable_entry;
-/*
- * Parses a binary resource table (resources.arsc) and adds the entries
- * to a ResourceTable. This is different than the libandroidfw ResTable
- * in that it scans the table from top to bottom and doesn't require
- * support for random access. It is also able to parse non-runtime
- * chunks and types.
- */
+// Parses a binary resource table (resources.arsc) and adds the entries to a ResourceTable.
+// This is different than the libandroidfw ResTable in that it scans the table from top to bottom
+// and doesn't require support for random access.
class BinaryResourceParser {
public:
- /*
- * Creates a parser, which will read `len` bytes from `data`, and
- * add any resources parsed to `table`. `source` is for logging purposes.
- */
+ // Creates a parser, which will read `len` bytes from `data`, and add any resources parsed to
+ // `table`. `source` is for logging purposes.
BinaryResourceParser(IAaptContext* context, ResourceTable* table, const Source& source,
const void* data, size_t data_len, io::IFileCollection* files = nullptr);
- /*
- * Parses the binary resource table and returns true if successful.
- */
+ // Parses the binary resource table and returns true if successful.
bool Parse();
private:
@@ -59,31 +51,25 @@ class BinaryResourceParser {
bool ParseTable(const android::ResChunk_header* chunk);
bool ParsePackage(const android::ResChunk_header* chunk);
bool ParseTypeSpec(const android::ResChunk_header* chunk);
- bool ParseType(const ResourceTablePackage* package,
- const android::ResChunk_header* chunk);
+ bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
bool ParseLibrary(const android::ResChunk_header* chunk);
std::unique_ptr<Item> ParseValue(const ResourceNameRef& name, const ConfigDescription& config,
const android::Res_value& value);
- std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Style> ParseStyle(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Style> ParseStyle(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Attribute> ParseAttr(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Attribute> ParseAttr(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Array> ParseArray(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Array> ParseArray(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Plural> ParsePlural(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Plural> ParsePlural(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
/**
@@ -125,13 +111,10 @@ class BinaryResourceParser {
namespace android {
-/**
- * Iterator functionality for ResTable_map_entry.
- */
+// Iterator functionality for ResTable_map_entry.
inline const ResTable_map* begin(const ResTable_map_entry* map) {
- return (const ResTable_map*)((const uint8_t*)map +
- aapt::util::DeviceToHost32(map->size));
+ return (const ResTable_map*)((const uint8_t*)map + ::aapt::util::DeviceToHost32(map->size));
}
inline const ResTable_map* end(const ResTable_map_entry* map) {
@@ -140,4 +123,4 @@ inline const ResTable_map* end(const ResTable_map_entry* map) {
} // namespace android
-#endif // AAPT_BINARY_RESOURCE_PARSER_H
+#endif // AAPT_FORMAT_BINARY_RESOURCEPARSER_H
diff --git a/tools/aapt2/flatten/ChunkWriter.h b/tools/aapt2/format/binary/ChunkWriter.h
index 968d3eef48ec..1892a295dcf5 100644
--- a/tools/aapt2/flatten/ChunkWriter.h
+++ b/tools/aapt2/format/binary/ChunkWriter.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_FLATTEN_CHUNKWRITER_H
-#define AAPT_FLATTEN_CHUNKWRITER_H
+#ifndef AAPT_FORMAT_BINARY_CHUNKWRITER_H
+#define AAPT_FORMAT_BINARY_CHUNKWRITER_H
#include "android-base/macros.h"
#include "androidfw/ResourceTypes.h"
@@ -27,7 +27,8 @@ namespace aapt {
class ChunkWriter {
public:
- explicit inline ChunkWriter(BigBuffer* buffer) : buffer_(buffer) {}
+ explicit inline ChunkWriter(BigBuffer* buffer) : buffer_(buffer) {
+ }
ChunkWriter(ChunkWriter&&) = default;
ChunkWriter& operator=(ChunkWriter&&) = default;
@@ -46,11 +47,17 @@ class ChunkWriter {
return buffer_->NextBlock<T>(count);
}
- inline BigBuffer* buffer() { return buffer_; }
+ inline BigBuffer* buffer() {
+ return buffer_;
+ }
- inline android::ResChunk_header* chunk_header() { return header_; }
+ inline android::ResChunk_header* chunk_header() {
+ return header_;
+ }
- inline size_t size() { return buffer_->size() - start_size_; }
+ inline size_t size() {
+ return buffer_->size() - start_size_;
+ }
inline android::ResChunk_header* Finish() {
buffer_->Align4();
@@ -77,4 +84,4 @@ inline android::ResChunk_header* ChunkWriter::StartChunk(uint16_t type) {
} // namespace aapt
-#endif /* AAPT_FLATTEN_CHUNKWRITER_H */
+#endif /* AAPT_FORMAT_BINARY_CHUNKWRITER_H */
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.cpp b/tools/aapt2/format/binary/ResChunkPullParser.cpp
index 8d92bd94b0ae..fd6919d1de60 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.cpp
+++ b/tools/aapt2/format/binary/ResChunkPullParser.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "unflatten/ResChunkPullParser.h"
+#include "format/binary/ResChunkPullParser.h"
#include <inttypes.h>
#include <cstddef>
@@ -44,9 +44,8 @@ ResChunkPullParser::Event ResChunkPullParser::Next() {
if (event_ == Event::kStartDocument) {
current_chunk_ = data_;
} else {
- current_chunk_ =
- (const ResChunk_header*)(((const char*)current_chunk_) +
- util::DeviceToHost32(current_chunk_->size));
+ current_chunk_ = (const ResChunk_header*)(((const char*)current_chunk_) +
+ util::DeviceToHost32(current_chunk_->size));
}
const std::ptrdiff_t diff = (const char*)current_chunk_ - (const char*)data_;
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.h b/tools/aapt2/format/binary/ResChunkPullParser.h
index 58277531034c..5ff13598a31d 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.h
+++ b/tools/aapt2/format/binary/ResChunkPullParser.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_RES_CHUNK_PULL_PARSER_H
-#define AAPT_RES_CHUNK_PULL_PARSER_H
+#ifndef AAPT_FORMAT_BINARY_RESCHUNKPULLPARSER_H
+#define AAPT_FORMAT_BINARY_RESCHUNKPULLPARSER_H
#include <string>
@@ -26,18 +26,13 @@
namespace aapt {
-/**
- * A pull parser, modeled after XmlPullParser, that reads
- * android::ResChunk_header structs from a block of data.
- *
- * An android::ResChunk_header specifies a type, headerSize,
- * and size. The pull parser will verify that the chunk's size
- * doesn't extend beyond the available data, and will iterate
- * over each chunk in the given block of data.
- *
- * Processing nested chunks is done by creating a new ResChunkPullParser
- * pointing to the data portion of a chunk.
- */
+// A pull parser, modeled after XmlPullParser, that reads android::ResChunk_header structs from a
+// block of data.
+// An android::ResChunk_header specifies a type, headerSize, and size. The pull parser will verify
+// that the chunk's size doesn't extend beyond the available data, and will iterate over each chunk
+// in the given block of data.
+// Processing nested chunks is done by creating a new ResChunkPullParser pointing to the data
+// portion of a chunk.
class ResChunkPullParser {
public:
enum class Event {
@@ -48,24 +43,18 @@ class ResChunkPullParser {
kChunk,
};
- /**
- * Returns false if the event is EndDocument or BadDocument.
- */
+ // Returns false if the event is EndDocument or BadDocument.
static bool IsGoodEvent(Event event);
- /**
- * Create a ResChunkPullParser to read android::ResChunk_headers
- * from the memory pointed to by data, of len bytes.
- */
+ // Create a ResChunkPullParser to read android::ResChunk_headers from the memory pointed to by
+ // data, of len bytes.
ResChunkPullParser(const void* data, size_t len);
Event event() const;
const std::string& error() const;
const android::ResChunk_header* chunk() const;
- /**
- * Move to the next android::ResChunk_header.
- */
+ // Move to the next android::ResChunk_header.
Event Next();
private:
@@ -86,15 +75,12 @@ inline static const T* ConvertTo(const android::ResChunk_header* chunk) {
return reinterpret_cast<const T*>(chunk);
}
-inline static const uint8_t* GetChunkData(
- const android::ResChunk_header* chunk) {
- return reinterpret_cast<const uint8_t*>(chunk) +
- util::DeviceToHost16(chunk->headerSize);
+inline static const uint8_t* GetChunkData(const android::ResChunk_header* chunk) {
+ return reinterpret_cast<const uint8_t*>(chunk) + util::DeviceToHost16(chunk->headerSize);
}
inline static uint32_t GetChunkDataLen(const android::ResChunk_header* chunk) {
- return util::DeviceToHost32(chunk->size) -
- util::DeviceToHost16(chunk->headerSize);
+ return util::DeviceToHost32(chunk->size) - util::DeviceToHost16(chunk->headerSize);
}
//
@@ -109,13 +95,16 @@ inline ResChunkPullParser::ResChunkPullParser(const void* data, size_t len)
: event_(Event::kStartDocument),
data_(reinterpret_cast<const android::ResChunk_header*>(data)),
len_(len),
- current_chunk_(nullptr) {}
+ current_chunk_(nullptr) {
+}
inline ResChunkPullParser::Event ResChunkPullParser::event() const {
return event_;
}
-inline const std::string& ResChunkPullParser::error() const { return error_; }
+inline const std::string& ResChunkPullParser::error() const {
+ return error_;
+}
inline const android::ResChunk_header* ResChunkPullParser::chunk() const {
return current_chunk_;
@@ -123,4 +112,4 @@ inline const android::ResChunk_header* ResChunkPullParser::chunk() const {
} // namespace aapt
-#endif // AAPT_RES_CHUNK_PULL_PARSER_H
+#endif // AAPT_FORMAT_BINARY_RESCHUNKPULLPARSER_H
diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/format/binary/ResourceTypeExtensions.h
index 6359b4143f1e..7f58df808cc7 100644
--- a/tools/aapt2/flatten/ResourceTypeExtensions.h
+++ b/tools/aapt2/format/binary/ResourceTypeExtensions.h
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-#ifndef AAPT_RESOURCE_TYPE_EXTENSIONS_H
-#define AAPT_RESOURCE_TYPE_EXTENSIONS_H
+#ifndef AAPT_FORMAT_BINARY_RESOURCETYPEEXTENSIONS_H
+#define AAPT_FORMAT_BINARY_RESOURCETYPEEXTENSIONS_H
#include "androidfw/ResourceTypes.h"
namespace aapt {
-/**
- * An alternative struct to use instead of ResTable_map_entry. This one is a
- * standard_layout
- * struct.
- */
+// An alternative struct to use instead of ResTable_map_entry. This one is a standard_layout struct.
struct ResTable_entry_ext {
android::ResTable_entry entry;
android::ResTable_ref parent;
@@ -34,4 +30,4 @@ struct ResTable_entry_ext {
} // namespace aapt
-#endif // AAPT_RESOURCE_TYPE_EXTENSIONS_H
+#endif // AAPT_FORMAT_BINARY_RESOURCETYPEEXTENSIONS_H
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 14b776b1bd99..57565a53794b 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "flatten/TableFlattener.h"
+#include "format/binary/TableFlattener.h"
#include <algorithm>
#include <numeric>
@@ -28,8 +28,8 @@
#include "ResourceValues.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
-#include "flatten/ChunkWriter.h"
-#include "flatten/ResourceTypeExtensions.h"
+#include "format/binary/ChunkWriter.h"
+#include "format/binary/ResourceTypeExtensions.h"
#include "util/BigBuffer.h"
using namespace android;
@@ -76,12 +76,13 @@ struct FlatEntry {
uint32_t entry_key;
};
-class MapFlattenVisitor : public RawValueVisitor {
+class MapFlattenVisitor : public ValueVisitor {
public:
- using RawValueVisitor::Visit;
+ using ValueVisitor::Visit;
MapFlattenVisitor(ResTable_entry_ext* out_entry, BigBuffer* buffer)
- : out_entry_(out_entry), buffer_(buffer) {}
+ : out_entry_(out_entry), buffer_(buffer) {
+ }
void Visit(Attribute* attr) override {
{
@@ -92,15 +93,13 @@ class MapFlattenVisitor : public RawValueVisitor {
if (attr->min_int != std::numeric_limits<int32_t>::min()) {
Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC,
- static_cast<uint32_t>(attr->min_int));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->min_int));
FlattenEntry(&key, &val);
}
if (attr->max_int != std::numeric_limits<int32_t>::max()) {
Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC,
- static_cast<uint32_t>(attr->max_int));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->max_int));
FlattenEntry(&key, &val);
}
@@ -188,7 +187,9 @@ class MapFlattenVisitor : public RawValueVisitor {
* Call this after visiting a Value. This will finish any work that
* needs to be done to prepare the entry.
*/
- void Finish() { out_entry_->count = util::HostToDevice32(entry_count_); }
+ void Finish() {
+ out_entry_->count = util::HostToDevice32(entry_count_);
+ }
private:
DISALLOW_COPY_AND_ASSIGN(MapFlattenVisitor);
@@ -223,7 +224,8 @@ class PackageFlattener {
diag_(context->GetDiagnostics()),
package_(package),
shared_libs_(shared_libs),
- use_sparse_entries_(use_sparse_entries) {}
+ use_sparse_entries_(use_sparse_entries) {
+ }
bool FlattenPackage(BigBuffer* buffer) {
ChunkWriter pkg_writer(buffer);
@@ -271,9 +273,9 @@ class PackageFlattener {
template <typename T, bool IsItem>
T* WriteEntry(FlatEntry* entry, BigBuffer* buffer) {
- static_assert(std::is_same<ResTable_entry, T>::value ||
- std::is_same<ResTable_entry_ext, T>::value,
- "T must be ResTable_entry or ResTable_entry_ext");
+ static_assert(
+ std::is_same<ResTable_entry, T>::value || std::is_same<ResTable_entry_ext, T>::value,
+ "T must be ResTable_entry or ResTable_entry_ext");
T* result = buffer->NextBlock<T>();
ResTable_entry* out_entry = (ResTable_entry*)result;
@@ -302,8 +304,7 @@ class PackageFlattener {
CHECK(item->Flatten(outValue)) << "flatten failed";
outValue->size = util::HostToDevice16(sizeof(*outValue));
} else {
- ResTable_entry_ext* out_entry =
- WriteEntry<ResTable_entry_ext, false>(entry, buffer);
+ ResTable_entry_ext* out_entry = WriteEntry<ResTable_entry_ext, false>(entry, buffer);
MapFlattenVisitor visitor(out_entry, buffer);
entry->value->Accept(&visitor);
visitor.Finish();
@@ -318,8 +319,7 @@ class PackageFlattener {
CHECK(num_total_entries <= std::numeric_limits<uint16_t>::max());
ChunkWriter type_writer(buffer);
- ResTable_type* type_header =
- type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
+ ResTable_type* type_header = type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
type_header->id = type->id.value();
type_header->config = config;
type_header->config.swapHtoD();
@@ -395,8 +395,7 @@ class PackageFlattener {
sorted_types.push_back(type.get());
}
- std::sort(sorted_types.begin(), sorted_types.end(),
- cmp_ids<ResourceTableType>);
+ std::sort(sorted_types.begin(), sorted_types.end(), cmp_ids<ResourceTableType>);
return sorted_types;
}
@@ -411,13 +410,11 @@ class PackageFlattener {
return sorted_entries;
}
- bool FlattenTypeSpec(ResourceTableType* type,
- std::vector<ResourceEntry*>* sorted_entries,
+ bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
BigBuffer* buffer) {
ChunkWriter type_spec_writer(buffer);
ResTable_typeSpec* spec_header =
- type_spec_writer.StartChunk<ResTable_typeSpec>(
- RES_TABLE_TYPE_SPEC_TYPE);
+ type_spec_writer.StartChunk<ResTable_typeSpec>(RES_TABLE_TYPE_SPEC_TYPE);
spec_header->id = type->id.value();
if (sorted_entries->empty()) {
@@ -443,8 +440,7 @@ class PackageFlattener {
// Populate the config masks for this entry.
if (entry->symbol_status.state == SymbolState::kPublic) {
- config_masks[entry->id.value()] |=
- util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
}
const size_t config_count = entry->values.size();
diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 223aef8119c0..88cbddfd14ab 100644
--- a/tools/aapt2/flatten/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_FLATTEN_TABLEFLATTENER_H
-#define AAPT_FLATTEN_TABLEFLATTENER_H
+#ifndef AAPT_FORMAT_BINARY_TABLEFLATTENER_H
+#define AAPT_FORMAT_BINARY_TABLEFLATTENER_H
#include "android-base/macros.h"
@@ -40,7 +40,8 @@ struct TableFlattenerOptions {
class TableFlattener : public IResourceTableConsumer {
public:
explicit TableFlattener(const TableFlattenerOptions& options, BigBuffer* buffer)
- : options_(options), buffer_(buffer) {}
+ : options_(options), buffer_(buffer) {
+ }
bool Consume(IAaptContext* context, ResourceTable* table) override;
@@ -53,4 +54,4 @@ class TableFlattener : public IResourceTableConsumer {
} // namespace aapt
-#endif /* AAPT_FLATTEN_TABLEFLATTENER_H */
+#endif /* AAPT_FORMAT_BINARY_TABLEFLATTENER_H */
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 4fdb2eced59b..6d7597372234 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include "flatten/TableFlattener.h"
+#include "format/binary/TableFlattener.h"
#include "android-base/stringprintf.h"
#include "ResourceUtils.h"
#include "SdkConstants.h"
+#include "format/binary/BinaryResourceParser.h"
#include "test/Test.h"
-#include "unflatten/BinaryResourceParser.h"
#include "util/Util.h"
using namespace android;
@@ -34,10 +34,8 @@ namespace aapt {
class TableFlattenerTest : public ::testing::Test {
public:
void SetUp() override {
- context_ = test::ContextBuilder()
- .SetCompilationPackage("com.app.test")
- .SetPackageId(0x7f)
- .Build();
+ context_ =
+ test::ContextBuilder().SetCompilationPackage("com.app.test").SetPackageId(0x7f).Build();
}
::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options,
@@ -80,12 +78,10 @@ class TableFlattenerTest : public ::testing::Test {
return ::testing::AssertionSuccess();
}
- ::testing::AssertionResult Exists(ResTable* table,
- const StringPiece& expected_name,
+ ::testing::AssertionResult Exists(ResTable* table, const StringPiece& expected_name,
const ResourceId& expected_id,
const ConfigDescription& expected_config,
- const uint8_t expected_data_type,
- const uint32_t expected_data,
+ const uint8_t expected_data_type, const uint32_t expected_data,
const uint32_t expected_spec_flags) {
const ResourceName expected_res_name = test::ParseNameOrDie(expected_name);
@@ -94,28 +90,26 @@ class TableFlattenerTest : public ::testing::Test {
ResTable_config config;
Res_value val;
uint32_t spec_flags;
- if (table->getResource(expected_id.id, &val, false, 0, &spec_flags,
- &config) < 0) {
+ if (table->getResource(expected_id.id, &val, false, 0, &spec_flags, &config) < 0) {
return ::testing::AssertionFailure() << "could not find resource with";
}
if (expected_data_type != val.dataType) {
return ::testing::AssertionFailure()
<< "expected data type " << std::hex << (int)expected_data_type
- << " but got data type " << (int)val.dataType << std::dec
- << " instead";
+ << " but got data type " << (int)val.dataType << std::dec << " instead";
}
if (expected_data != val.data) {
return ::testing::AssertionFailure()
- << "expected data " << std::hex << expected_data
- << " but got data " << val.data << std::dec << " instead";
+ << "expected data " << std::hex << expected_data << " but got data " << val.data
+ << std::dec << " instead";
}
if (expected_spec_flags != spec_flags) {
return ::testing::AssertionFailure()
- << "expected specFlags " << std::hex << expected_spec_flags
- << " but got specFlags " << spec_flags << std::dec << " instead";
+ << "expected specFlags " << std::hex << expected_spec_flags << " but got specFlags "
+ << spec_flags << std::dec << " instead";
}
ResTable::resource_name actual_name;
@@ -127,16 +121,14 @@ class TableFlattenerTest : public ::testing::Test {
if (!resName) {
return ::testing::AssertionFailure()
<< "expected name '" << expected_res_name << "' but got '"
- << StringPiece16(actual_name.package, actual_name.packageLen)
- << ":" << StringPiece16(actual_name.type, actual_name.typeLen)
- << "/" << StringPiece16(actual_name.name, actual_name.nameLen)
- << "'";
+ << StringPiece16(actual_name.package, actual_name.packageLen) << ":"
+ << StringPiece16(actual_name.type, actual_name.typeLen) << "/"
+ << StringPiece16(actual_name.name, actual_name.nameLen) << "'";
}
if (expected_config != config) {
- return ::testing::AssertionFailure() << "expected config '"
- << expected_config << "' but got '"
- << ConfigDescription(config) << "'";
+ return ::testing::AssertionFailure() << "expected config '" << expected_config
+ << "' but got '" << ConfigDescription(config) << "'";
}
return ::testing::AssertionSuccess();
}
@@ -152,57 +144,46 @@ TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) {
.AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
.AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
.AddValue("com.app.test:id/three", ResourceId(0x7f020002),
- test::BuildReference("com.app.test:id/one",
- ResourceId(0x7f020000)))
+ test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
.AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
- util::make_unique<BinaryPrimitive>(
- uint8_t(Res_value::TYPE_INT_DEC), 1u))
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
.AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
ResourceId(0x7f030000),
- util::make_unique<BinaryPrimitive>(
- uint8_t(Res_value::TYPE_INT_DEC), 2u))
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
.AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
- .AddString("com.app.test:layout/bar", ResourceId(0x7f050000),
- "res/layout/bar.xml")
+ .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
.Build();
ResTable res_table;
ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000),
- {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001),
- {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
- ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE,
- 0x7f020000u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {},
+ Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
- ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
- ResTable_config::CONFIG_VERSION));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), {},
+ Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
- ResourceId(0x7f030000), test::ParseConfigOrDie("v1"),
- Res_value::TYPE_INT_DEC, 2u,
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000),
+ test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
ResTable_config::CONFIG_VERSION));
std::u16string foo_str = u"foo";
- ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(),
- foo_str.size());
+ ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
ASSERT_GE(idx, 0);
- EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test",
- ResourceId(0x7f040000), {}, Res_value::TYPE_STRING,
- (uint32_t)idx, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
+ Res_value::TYPE_STRING, (uint32_t)idx, 0u));
std::u16string bar_path = u"res/layout/bar.xml";
- idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(),
- bar_path.size());
+ idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
ASSERT_GE(idx, 0);
- EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar",
- ResourceId(0x7f050000), {}, Res_value::TYPE_STRING,
- (uint32_t)idx, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar", ResourceId(0x7f050000), {},
+ Res_value::TYPE_STRING, (uint32_t)idx, 0u));
}
TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
@@ -216,11 +197,10 @@ TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
ResTable res_table;
ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001),
- {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
- ResourceId(0x7f020003), {}, Res_value::TYPE_INT_BOOLEAN,
- 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020003), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
}
TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
@@ -231,8 +211,7 @@ TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("android", 0x01)
- .AddValue("android:attr/foo", ResourceId(0x01010000),
- util::make_unique<Attribute>(attr))
+ .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>(attr))
.Build();
ResourceTable result;
@@ -308,9 +287,12 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) {
ASSERT_THAT(value, NotNull());
EXPECT_EQ(0u, value->value.data);
- ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1", sparse_config), IsNull());
+ ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1",
+ sparse_config),
+ IsNull());
- value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4", sparse_config);
+ value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4",
+ sparse_config);
ASSERT_THAT(value, NotNull());
EXPECT_EQ(4u, value->value.data);
}
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
index b3b308a29fc5..2456c3dfd5e9 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "flatten/XmlFlattener.h"
+#include "format/binary/XmlFlattener.h"
#include <algorithm>
#include <map>
@@ -26,8 +26,8 @@
#include "utils/misc.h"
#include "SdkConstants.h"
-#include "flatten/ChunkWriter.h"
-#include "flatten/ResourceTypeExtensions.h"
+#include "format/binary/ChunkWriter.h"
+#include "format/binary/ResourceTypeExtensions.h"
#include "xml/XmlDom.h"
using namespace android;
@@ -71,7 +71,8 @@ class XmlFlattenerVisitor : public xml::Visitor {
std::vector<StringFlattenDest> string_refs;
XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options)
- : buffer_(buffer), options_(options) {}
+ : buffer_(buffer), options_(options) {
+ }
void Visit(xml::Text* node) override {
if (util::TrimWhitespace(node->text).empty()) {
@@ -152,15 +153,14 @@ class XmlFlattenerVisitor : public xml::Visitor {
private:
DISALLOW_COPY_AND_ASSIGN(XmlFlattenerVisitor);
- void AddString(const StringPiece& str, uint32_t priority,
- android::ResStringPool_ref* dest,
+ void AddString(const StringPiece& str, uint32_t priority, android::ResStringPool_ref* dest,
bool treat_empty_string_as_null = false) {
if (str.empty() && treat_empty_string_as_null) {
// Some parts of the runtime treat null differently than empty string.
dest->index = util::DeviceToHost32(-1);
} else {
- string_refs.push_back(StringFlattenDest{
- pool.MakeRef(str, StringPool::Context(priority)), dest});
+ string_refs.push_back(
+ StringFlattenDest{pool.MakeRef(str, StringPool::Context(priority)), dest});
}
}
@@ -208,8 +208,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
uint16_t attribute_index = 1;
for (const xml::Attribute* xml_attr : filtered_attrs_) {
// Assign the indices for specific attributes.
- if (xml_attr->compiled_attribute &&
- xml_attr->compiled_attribute.value().id &&
+ if (xml_attr->compiled_attribute && xml_attr->compiled_attribute.value().id &&
xml_attr->compiled_attribute.value().id.value() == kIdAttr) {
flat_elem->idIndex = util::HostToDevice16(attribute_index);
} else if (xml_attr->namespace_uri.empty()) {
@@ -241,9 +240,8 @@ class XmlFlattenerVisitor : public xml::Visitor {
// Lookup the StringPool for this package and make the reference there.
const xml::AaptAttribute& aapt_attr = xml_attr->compiled_attribute.value();
- StringPool::Ref name_ref =
- package_pools[aapt_attr.id.value().package_id()].MakeRef(
- xml_attr->name, StringPool::Context(aapt_attr.id.value().id));
+ StringPool::Ref name_ref = package_pools[aapt_attr.id.value().package_id()].MakeRef(
+ xml_attr->name, StringPool::Context(aapt_attr.id.value().id));
// Add it to the list of strings to flatten.
AddString(name_ref, &flat_attr->name);
@@ -272,7 +270,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
flat_attr->typedValue.dataType = android::Res_value::TYPE_STRING;
AddString(str_builder.ToString(), kLowPriority,
- (ResStringPool_ref*) &flat_attr->typedValue.data);
+ (ResStringPool_ref*)&flat_attr->typedValue.data);
}
flat_attr->typedValue.size = util::HostToDevice16(sizeof(flat_attr->typedValue));
@@ -314,7 +312,11 @@ bool XmlFlattener::Flatten(IAaptContext* context, xml::Node* node) {
xml_header_writer.StartChunk<ResXMLTree_header>(RES_XML_TYPE);
// Flatten the StringPool.
- StringPool::FlattenUtf8(buffer_, visitor.pool);
+ if (options_.use_utf16) {
+ StringPool::FlattenUtf16(buffer_, visitor.pool);
+ } else {
+ StringPool::FlattenUtf8(buffer_, visitor.pool);
+ }
{
// Write the array of resource IDs, indexed by StringPool order.
diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/format/binary/XmlFlattener.h
index 87557f29f3be..8db2281cd74a 100644
--- a/tools/aapt2/flatten/XmlFlattener.h
+++ b/tools/aapt2/format/binary/XmlFlattener.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_FLATTEN_XMLFLATTENER_H
-#define AAPT_FLATTEN_XMLFLATTENER_H
+#ifndef AAPT_FORMAT_BINARY_XMLFLATTENER_H
+#define AAPT_FORMAT_BINARY_XMLFLATTENER_H
#include "android-base/macros.h"
@@ -26,16 +26,19 @@
namespace aapt {
struct XmlFlattenerOptions {
- /**
- * Keep attribute raw string values along with typed values.
- */
+ // Keep attribute raw string values along with typed values.
bool keep_raw_values = false;
+
+ // Encode the strings in UTF-16. Only needed for AndroidManifest.xml to avoid a bug in
+ // certain non-AOSP platforms: https://issuetracker.google.com/64434571
+ bool use_utf16 = false;
};
class XmlFlattener : public IXmlResourceConsumer {
public:
XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options)
- : buffer_(buffer), options_(options) {}
+ : buffer_(buffer), options_(options) {
+ }
bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
@@ -50,4 +53,4 @@ class XmlFlattener : public IXmlResourceConsumer {
} // namespace aapt
-#endif /* AAPT_FLATTEN_XMLFLATTENER_H */
+#endif /* AAPT_FORMAT_BINARY_XMLFLATTENER_H */
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index a57e3178accd..0450f6c16de5 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "flatten/XmlFlattener.h"
+#include "format/binary/XmlFlattener.h"
#include "androidfw/ResourceTypes.h"
@@ -55,8 +55,7 @@ class XmlFlattenerTest : public ::testing::Test {
.Build();
}
- ::testing::AssertionResult Flatten(xml::XmlResource* doc,
- android::ResXMLTree* out_tree,
+ ::testing::AssertionResult Flatten(xml::XmlResource* doc, android::ResXMLTree* out_tree,
const XmlFlattenerOptions& options = {}) {
using namespace android; // For NO_ERROR on windows because it is a macro.
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
new file mode 100644
index 000000000000..c14f09a3b103
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 2016 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 "format/proto/ProtoDeserialize.h"
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "Locale.h"
+#include "ResourceTable.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
+
+using ::android::ResStringPool;
+using ::google::protobuf::io::CodedInputStream;
+
+namespace aapt {
+
+namespace {
+
+class ReferenceIdToNameVisitor : public DescendingValueVisitor {
+ public:
+ using DescendingValueVisitor::Visit;
+
+ explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping)
+ : mapping_(mapping) {
+ CHECK(mapping_ != nullptr);
+ }
+
+ void Visit(Reference* reference) override {
+ if (!reference->id || !reference->id.value().is_valid()) {
+ return;
+ }
+
+ ResourceId id = reference->id.value();
+ auto cache_iter = mapping_->find(id);
+ if (cache_iter != mapping_->end()) {
+ reference->name = cache_iter->second.ToResourceName();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReferenceIdToNameVisitor);
+
+ const std::map<ResourceId, ResourceNameRef>* mapping_;
+};
+
+} // namespace
+
+bool DeserializeConfigFromPb(const pb::Configuration& pb_config, ConfigDescription* out_config,
+ std::string* out_error) {
+ out_config->mcc = static_cast<uint16_t>(pb_config.mcc());
+ out_config->mnc = static_cast<uint16_t>(pb_config.mnc());
+
+ if (!pb_config.locale().empty()) {
+ LocaleValue lv;
+ if (!lv.InitFromBcp47Tag(pb_config.locale())) {
+ std::ostringstream error;
+ error << "configuration has invalid locale '" << pb_config.locale() << "'";
+ *out_error = error.str();
+ return false;
+ }
+ lv.WriteTo(out_config);
+ }
+
+ switch (pb_config.layout_direction()) {
+ case pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_LTR:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_LAYOUTDIR) |
+ ConfigDescription::LAYOUTDIR_LTR;
+ break;
+
+ case pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_RTL:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_LAYOUTDIR) |
+ ConfigDescription::LAYOUTDIR_RTL;
+ break;
+
+ default:
+ break;
+ }
+
+ out_config->smallestScreenWidthDp = static_cast<uint16_t>(pb_config.smallest_screen_width_dp());
+ out_config->screenWidthDp = static_cast<uint16_t>(pb_config.screen_width_dp());
+ out_config->screenHeightDp = static_cast<uint16_t>(pb_config.screen_height_dp());
+
+ switch (pb_config.screen_layout_size()) {
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_SMALL:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_SMALL;
+ break;
+
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_NORMAL:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_NORMAL;
+ break;
+
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_LARGE:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_LARGE;
+ break;
+
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_XLARGE:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_XLARGE;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.screen_layout_long()) {
+ case pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_LONG:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENLONG) |
+ ConfigDescription::SCREENLONG_YES;
+ break;
+
+ case pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_NOTLONG:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENLONG) |
+ ConfigDescription::SCREENLONG_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.screen_round()) {
+ case pb::Configuration_ScreenRound_SCREEN_ROUND_ROUND:
+ out_config->screenLayout2 =
+ (out_config->screenLayout2 & ~ConfigDescription::MASK_SCREENROUND) |
+ ConfigDescription::SCREENROUND_YES;
+ break;
+
+ case pb::Configuration_ScreenRound_SCREEN_ROUND_NOTROUND:
+ out_config->screenLayout2 =
+ (out_config->screenLayout2 & ~ConfigDescription::MASK_SCREENROUND) |
+ ConfigDescription::SCREENROUND_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.wide_color_gamut()) {
+ case pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_WIDECG:
+ out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_WIDE_COLOR_GAMUT) |
+ ConfigDescription::WIDE_COLOR_GAMUT_YES;
+ break;
+
+ case pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_NOWIDECG:
+ out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_WIDE_COLOR_GAMUT) |
+ ConfigDescription::WIDE_COLOR_GAMUT_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.hdr()) {
+ case pb::Configuration_Hdr_HDR_HIGHDR:
+ out_config->colorMode =
+ (out_config->colorMode & ~ConfigDescription::MASK_HDR) | ConfigDescription::HDR_YES;
+ break;
+
+ case pb::Configuration_Hdr_HDR_LOWDR:
+ out_config->colorMode =
+ (out_config->colorMode & ~ConfigDescription::MASK_HDR) | ConfigDescription::HDR_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.orientation()) {
+ case pb::Configuration_Orientation_ORIENTATION_PORT:
+ out_config->orientation = ConfigDescription::ORIENTATION_PORT;
+ break;
+
+ case pb::Configuration_Orientation_ORIENTATION_LAND:
+ out_config->orientation = ConfigDescription::ORIENTATION_LAND;
+ break;
+
+ case pb::Configuration_Orientation_ORIENTATION_SQUARE:
+ out_config->orientation = ConfigDescription::ORIENTATION_SQUARE;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.ui_mode_type()) {
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_NORMAL:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_NORMAL;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_DESK:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_DESK;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_CAR:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_CAR;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_TELEVISION:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_TELEVISION;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_APPLIANCE:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_APPLIANCE;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_WATCH:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_WATCH;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_VRHEADSET:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_VR_HEADSET;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.ui_mode_night()) {
+ case pb::Configuration_UiModeNight_UI_MODE_NIGHT_NIGHT:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_NIGHT) |
+ ConfigDescription::UI_MODE_NIGHT_YES;
+ break;
+
+ case pb::Configuration_UiModeNight_UI_MODE_NIGHT_NOTNIGHT:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_NIGHT) |
+ ConfigDescription::UI_MODE_NIGHT_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ out_config->density = static_cast<uint16_t>(pb_config.density());
+
+ switch (pb_config.touchscreen()) {
+ case pb::Configuration_Touchscreen_TOUCHSCREEN_NOTOUCH:
+ out_config->touchscreen = ConfigDescription::TOUCHSCREEN_NOTOUCH;
+ break;
+
+ case pb::Configuration_Touchscreen_TOUCHSCREEN_STYLUS:
+ out_config->touchscreen = ConfigDescription::TOUCHSCREEN_STYLUS;
+ break;
+
+ case pb::Configuration_Touchscreen_TOUCHSCREEN_FINGER:
+ out_config->touchscreen = ConfigDescription::TOUCHSCREEN_FINGER;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.keys_hidden()) {
+ case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSEXPOSED:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) |
+ ConfigDescription::KEYSHIDDEN_NO;
+ break;
+
+ case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSHIDDEN:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) |
+ ConfigDescription::KEYSHIDDEN_YES;
+ break;
+
+ case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSSOFT:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) |
+ ConfigDescription::KEYSHIDDEN_SOFT;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.keyboard()) {
+ case pb::Configuration_Keyboard_KEYBOARD_NOKEYS:
+ out_config->keyboard = ConfigDescription::KEYBOARD_NOKEYS;
+ break;
+
+ case pb::Configuration_Keyboard_KEYBOARD_QWERTY:
+ out_config->keyboard = ConfigDescription::KEYBOARD_QWERTY;
+ break;
+
+ case pb::Configuration_Keyboard_KEYBOARD_TWELVEKEY:
+ out_config->keyboard = ConfigDescription::KEYBOARD_12KEY;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.nav_hidden()) {
+ case pb::Configuration_NavHidden_NAV_HIDDEN_NAVEXPOSED:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_NAVHIDDEN) |
+ ConfigDescription::NAVHIDDEN_NO;
+ break;
+
+ case pb::Configuration_NavHidden_NAV_HIDDEN_NAVHIDDEN:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_NAVHIDDEN) |
+ ConfigDescription::NAVHIDDEN_YES;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.navigation()) {
+ case pb::Configuration_Navigation_NAVIGATION_NONAV:
+ out_config->navigation = ConfigDescription::NAVIGATION_NONAV;
+ break;
+
+ case pb::Configuration_Navigation_NAVIGATION_DPAD:
+ out_config->navigation = ConfigDescription::NAVIGATION_DPAD;
+ break;
+
+ case pb::Configuration_Navigation_NAVIGATION_TRACKBALL:
+ out_config->navigation = ConfigDescription::NAVIGATION_TRACKBALL;
+ break;
+
+ case pb::Configuration_Navigation_NAVIGATION_WHEEL:
+ out_config->navigation = ConfigDescription::NAVIGATION_WHEEL;
+ break;
+
+ default:
+ break;
+ }
+
+ out_config->screenWidth = static_cast<uint16_t>(pb_config.screen_width());
+ out_config->screenHeight = static_cast<uint16_t>(pb_config.screen_height());
+ out_config->sdkVersion = static_cast<uint16_t>(pb_config.sdk_version());
+ return true;
+}
+
+static void DeserializeSourceFromPb(const pb::Source& pb_source, const ResStringPool& src_pool,
+ Source* out_source) {
+ out_source->path = util::GetString(src_pool, pb_source.path_idx());
+ out_source->line = static_cast<size_t>(pb_source.position().line_number());
+}
+
+static SymbolState DeserializeVisibilityFromPb(const pb::SymbolStatus_Visibility& pb_visibility) {
+ switch (pb_visibility) {
+ case pb::SymbolStatus_Visibility_PRIVATE:
+ return SymbolState::kPrivate;
+ case pb::SymbolStatus_Visibility_PUBLIC:
+ return SymbolState::kPublic;
+ default:
+ break;
+ }
+ return SymbolState::kUndefined;
+}
+
+static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool,
+ ResourceTable* out_table, std::string* out_error) {
+ Maybe<uint8_t> id;
+ if (pb_package.has_package_id()) {
+ id = static_cast<uint8_t>(pb_package.package_id().id());
+ }
+
+ std::map<ResourceId, ResourceNameRef> id_index;
+
+ ResourceTablePackage* pkg = out_table->CreatePackage(pb_package.package_name(), id);
+ for (const pb::Type& pb_type : pb_package.type()) {
+ const ResourceType* res_type = ParseResourceType(pb_type.name());
+ if (res_type == nullptr) {
+ std::ostringstream error;
+ error << "unknown type '" << pb_type.name() << "'";
+ *out_error = error.str();
+ return false;
+ }
+
+ ResourceTableType* type = pkg->FindOrCreateType(*res_type);
+ for (const pb::Entry& pb_entry : pb_type.entry()) {
+ ResourceEntry* entry = type->FindOrCreateEntry(pb_entry.name());
+
+ // Deserialize the symbol status (public/private with source and comments).
+ if (pb_entry.has_symbol_status()) {
+ const pb::SymbolStatus& pb_status = pb_entry.symbol_status();
+ if (pb_status.has_source()) {
+ DeserializeSourceFromPb(pb_status.source(), src_pool, &entry->symbol_status.source);
+ }
+
+ entry->symbol_status.comment = pb_status.comment();
+ entry->symbol_status.allow_new = pb_status.allow_new();
+
+ const SymbolState visibility = DeserializeVisibilityFromPb(pb_status.visibility());
+ entry->symbol_status.state = visibility;
+
+ if (visibility == SymbolState::kPublic) {
+ // This is a public symbol, we must encode the ID now if there is one.
+ if (pb_entry.has_entry_id()) {
+ entry->id = static_cast<uint16_t>(pb_entry.entry_id().id());
+ }
+
+ if (type->symbol_status.state != SymbolState::kPublic) {
+ // If the type has not been made public, do so now.
+ type->symbol_status.state = SymbolState::kPublic;
+ if (pb_type.has_type_id()) {
+ type->id = static_cast<uint8_t>(pb_type.type_id().id());
+ }
+ }
+ } else if (visibility == SymbolState::kPrivate) {
+ if (type->symbol_status.state == SymbolState::kUndefined) {
+ type->symbol_status.state = SymbolState::kPrivate;
+ }
+ }
+ }
+
+ ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
+ pb_entry.entry_id().id());
+ if (resid.is_valid()) {
+ id_index[resid] = ResourceNameRef(pkg->name, type->type, entry->name);
+ }
+
+ for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) {
+ const pb::Configuration& pb_config = pb_config_value.config();
+
+ ConfigDescription config;
+ if (!DeserializeConfigFromPb(pb_config, &config, out_error)) {
+ return false;
+ }
+
+ ResourceConfigValue* config_value = entry->FindOrCreateValue(config, pb_config.product());
+ if (config_value->value != nullptr) {
+ *out_error = "duplicate configuration in resource table";
+ return false;
+ }
+
+ config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config,
+ &out_table->string_pool, out_error);
+ if (config_value->value == nullptr) {
+ return false;
+ }
+ }
+ }
+ }
+
+ ReferenceIdToNameVisitor visitor(&id_index);
+ VisitAllValuesInPackage(pkg, &visitor);
+ return true;
+}
+
+bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* out_table,
+ std::string* out_error) {
+ // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
+ // causes errors when qualifying it with android::
+ using namespace android;
+
+ ResStringPool source_pool;
+ if (pb_table.has_source_pool()) {
+ status_t result = source_pool.setTo(pb_table.source_pool().data().data(),
+ pb_table.source_pool().data().size());
+ if (result != NO_ERROR) {
+ *out_error = "invalid source pool";
+ return false;
+ }
+ }
+
+ for (const pb::Package& pb_package : pb_table.package()) {
+ if (!DeserializePackageFromPb(pb_package, source_pool, out_table, out_error)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file,
+ ResourceFile* out_file, std::string* out_error) {
+ ResourceNameRef name_ref;
+ if (!ResourceUtils::ParseResourceName(pb_file.resource_name(), &name_ref)) {
+ std::ostringstream error;
+ error << "invalid resource name in compiled file header: " << pb_file.resource_name();
+ *out_error = error.str();
+ return false;
+ }
+
+ out_file->name = name_ref.ToResourceName();
+ out_file->source.path = pb_file.source_path();
+
+ std::string config_error;
+ if (!DeserializeConfigFromPb(pb_file.config(), &out_file->config, &config_error)) {
+ std::ostringstream error;
+ error << "invalid resource configuration in compiled file header: " << config_error;
+ *out_error = error.str();
+ return false;
+ }
+
+ for (const pb::internal::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbol()) {
+ if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(), &name_ref)) {
+ std::ostringstream error;
+ error << "invalid resource name for exported symbol in compiled file header: "
+ << pb_file.resource_name();
+ *out_error = error.str();
+ return false;
+ }
+
+ size_t line = 0u;
+ if (pb_symbol.has_source()) {
+ line = pb_symbol.source().line_number();
+ }
+ out_file->exported_symbols.push_back(SourcedResourceName{name_ref.ToResourceName(), line});
+ }
+ return true;
+}
+
+static Reference::Type DeserializeReferenceTypeFromPb(const pb::Reference_Type& pb_type) {
+ switch (pb_type) {
+ case pb::Reference_Type_REFERENCE:
+ return Reference::Type::kResource;
+ case pb::Reference_Type_ATTRIBUTE:
+ return Reference::Type::kAttribute;
+ default:
+ break;
+ }
+ return Reference::Type::kResource;
+}
+
+static bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* out_ref,
+ std::string* out_error) {
+ out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
+ out_ref->private_reference = pb_ref.private_();
+
+ if (pb_ref.id() != 0) {
+ out_ref->id = ResourceId(pb_ref.id());
+ }
+
+ if (!pb_ref.name().empty()) {
+ ResourceNameRef name_ref;
+ if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) {
+ std::ostringstream error;
+ error << "reference has invalid resource name '" << pb_ref.name() << "'";
+ *out_error = error.str();
+ return false;
+ }
+ out_ref->name = name_ref.ToResourceName();
+ }
+ return true;
+}
+
+template <typename T>
+static void DeserializeItemMetaDataFromPb(const T& pb_item, const android::ResStringPool& src_pool,
+ Value* out_value) {
+ if (pb_item.has_source()) {
+ Source source;
+ DeserializeSourceFromPb(pb_item.source(), src_pool, &source);
+ out_value->SetSource(std::move(source));
+ }
+ out_value->SetComment(pb_item.comment());
+}
+
+static size_t DeserializePluralEnumFromPb(const pb::Plural_Arity& arity) {
+ switch (arity) {
+ case pb::Plural_Arity_ZERO:
+ return Plural::Zero;
+ case pb::Plural_Arity_ONE:
+ return Plural::One;
+ case pb::Plural_Arity_TWO:
+ return Plural::Two;
+ case pb::Plural_Arity_FEW:
+ return Plural::Few;
+ case pb::Plural_Arity_MANY:
+ return Plural::Many;
+ default:
+ break;
+ }
+ return Plural::Other;
+}
+
+std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config,
+ StringPool* value_pool, std::string* out_error) {
+ std::unique_ptr<Value> value;
+ if (pb_value.has_item()) {
+ value = DeserializeItemFromPb(pb_value.item(), src_pool, config, value_pool, out_error);
+ if (value == nullptr) {
+ return {};
+ }
+
+ } else if (pb_value.has_compound_value()) {
+ const pb::CompoundValue& pb_compound_value = pb_value.compound_value();
+ switch (pb_compound_value.value_case()) {
+ case pb::CompoundValue::kAttr: {
+ const pb::Attribute& pb_attr = pb_compound_value.attr();
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>();
+ attr->type_mask = pb_attr.format_flags();
+ attr->min_int = pb_attr.min_int();
+ attr->max_int = pb_attr.max_int();
+ for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbol()) {
+ Attribute::Symbol symbol;
+ DeserializeItemMetaDataFromPb(pb_symbol, src_pool, &symbol.symbol);
+ if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol, out_error)) {
+ return {};
+ }
+ symbol.value = pb_symbol.value();
+ attr->symbols.push_back(std::move(symbol));
+ }
+ value = std::move(attr);
+ } break;
+
+ case pb::CompoundValue::kStyle: {
+ const pb::Style& pb_style = pb_compound_value.style();
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+ if (pb_style.has_parent()) {
+ style->parent = Reference();
+ if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value(), out_error)) {
+ return {};
+ }
+
+ if (pb_style.has_parent_source()) {
+ Source parent_source;
+ DeserializeSourceFromPb(pb_style.parent_source(), src_pool, &parent_source);
+ style->parent.value().SetSource(std::move(parent_source));
+ }
+ }
+
+ for (const pb::Style_Entry& pb_entry : pb_style.entry()) {
+ Style::Entry entry;
+ if (!DeserializeReferenceFromPb(pb_entry.key(), &entry.key, out_error)) {
+ return {};
+ }
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, &entry.key);
+ entry.value =
+ DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error);
+ if (entry.value == nullptr) {
+ return {};
+ }
+
+ // Copy the meta-data into the value as well.
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, entry.value.get());
+ style->entries.push_back(std::move(entry));
+ }
+ value = std::move(style);
+ } break;
+
+ case pb::CompoundValue::kStyleable: {
+ const pb::Styleable& pb_styleable = pb_compound_value.styleable();
+ std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+ for (const pb::Styleable_Entry& pb_entry : pb_styleable.entry()) {
+ Reference attr_ref;
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, &attr_ref);
+ DeserializeReferenceFromPb(pb_entry.attr(), &attr_ref, out_error);
+ styleable->entries.push_back(std::move(attr_ref));
+ }
+ value = std::move(styleable);
+ } break;
+
+ case pb::CompoundValue::kArray: {
+ const pb::Array& pb_array = pb_compound_value.array();
+ std::unique_ptr<Array> array = util::make_unique<Array>();
+ for (const pb::Array_Element& pb_entry : pb_array.element()) {
+ std::unique_ptr<Item> item =
+ DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error);
+ if (item == nullptr) {
+ return {};
+ }
+
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, item.get());
+ array->elements.push_back(std::move(item));
+ }
+ value = std::move(array);
+ } break;
+
+ case pb::CompoundValue::kPlural: {
+ const pb::Plural& pb_plural = pb_compound_value.plural();
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ for (const pb::Plural_Entry& pb_entry : pb_plural.entry()) {
+ size_t plural_idx = DeserializePluralEnumFromPb(pb_entry.arity());
+ plural->values[plural_idx] =
+ DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error);
+ if (!plural->values[plural_idx]) {
+ return {};
+ }
+
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, plural->values[plural_idx].get());
+ }
+ value = std::move(plural);
+ } break;
+
+ default:
+ LOG(FATAL) << "unknown compound value: " << (int)pb_compound_value.value_case();
+ break;
+ }
+ } else {
+ LOG(FATAL) << "unknown value: " << (int)pb_value.value_case();
+ return {};
+ }
+
+ CHECK(value) << "forgot to set value";
+
+ value->SetWeak(pb_value.weak());
+ DeserializeItemMetaDataFromPb(pb_value, src_pool, value.get());
+ return value;
+}
+
+std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config, StringPool* value_pool,
+ std::string* out_error) {
+ switch (pb_item.value_case()) {
+ case pb::Item::kRef: {
+ const pb::Reference& pb_ref = pb_item.ref();
+ std::unique_ptr<Reference> ref = util::make_unique<Reference>();
+ if (!DeserializeReferenceFromPb(pb_ref, ref.get(), out_error)) {
+ return {};
+ }
+ return std::move(ref);
+ } break;
+
+ case pb::Item::kPrim: {
+ const pb::Primitive& pb_prim = pb_item.prim();
+ return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()),
+ pb_prim.data());
+ } break;
+
+ case pb::Item::kId: {
+ return util::make_unique<Id>();
+ } break;
+
+ case pb::Item::kStr: {
+ return util::make_unique<String>(
+ value_pool->MakeRef(pb_item.str().value(), StringPool::Context(config)));
+ } break;
+
+ case pb::Item::kRawStr: {
+ return util::make_unique<RawString>(
+ value_pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config)));
+ } break;
+
+ case pb::Item::kStyledStr: {
+ const pb::StyledString& pb_str = pb_item.styled_str();
+ StyleString style_str{pb_str.value()};
+ for (const pb::StyledString::Span& pb_span : pb_str.span()) {
+ style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()});
+ }
+ return util::make_unique<StyledString>(value_pool->MakeRef(
+ style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
+ } break;
+
+ case pb::Item::kFile: {
+ return util::make_unique<FileReference>(value_pool->MakeRef(
+ pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
+ } break;
+
+ default:
+ LOG(FATAL) << "unknown item: " << (int)pb_item.value_case();
+ break;
+ }
+ return {};
+}
+
+std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node,
+ std::string* out_error) {
+ if (!pb_node.has_element()) {
+ return {};
+ }
+
+ std::unique_ptr<xml::XmlResource> resource = util::make_unique<xml::XmlResource>();
+ resource->root = util::make_unique<xml::Element>();
+ if (!DeserializeXmlFromPb(pb_node, resource->root.get(), &resource->string_pool, out_error)) {
+ return {};
+ }
+ return resource;
+}
+
+bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, StringPool* value_pool,
+ std::string* out_error) {
+ const pb::XmlElement& pb_el = pb_node.element();
+ out_el->name = pb_el.name();
+ out_el->namespace_uri = pb_el.namespace_uri();
+ out_el->line_number = pb_node.source().line_number();
+ out_el->column_number = pb_node.source().column_number();
+
+ for (const pb::XmlNamespace& pb_ns : pb_el.namespace_declaration()) {
+ xml::NamespaceDecl decl;
+ decl.uri = pb_ns.uri();
+ decl.prefix = pb_ns.prefix();
+ decl.line_number = pb_ns.source().line_number();
+ decl.column_number = pb_ns.source().column_number();
+ out_el->namespace_decls.push_back(std::move(decl));
+ }
+
+ for (const pb::XmlAttribute& pb_attr : pb_el.attribute()) {
+ xml::Attribute attr;
+ attr.name = pb_attr.name();
+ attr.namespace_uri = pb_attr.namespace_uri();
+ attr.value = pb_attr.value();
+ if (pb_attr.resource_id() != 0u) {
+ attr.compiled_attribute = xml::AaptAttribute{Attribute(), ResourceId(pb_attr.resource_id())};
+ }
+ if (pb_attr.has_compiled_item()) {
+ attr.compiled_value =
+ DeserializeItemFromPb(pb_attr.compiled_item(), {}, {}, value_pool, out_error);
+ if (attr.compiled_value == nullptr) {
+ return {};
+ }
+ attr.compiled_value->SetSource(Source().WithLine(pb_attr.source().line_number()));
+ }
+ out_el->attributes.push_back(std::move(attr));
+ }
+
+ // Deserialize the children.
+ for (const pb::XmlNode& pb_child : pb_el.child()) {
+ switch (pb_child.node_case()) {
+ case pb::XmlNode::NodeCase::kText: {
+ std::unique_ptr<xml::Text> text = util::make_unique<xml::Text>();
+ text->line_number = pb_child.source().line_number();
+ text->column_number = pb_child.source().column_number();
+ text->text = pb_child.text();
+ out_el->AppendChild(std::move(text));
+ } break;
+
+ case pb::XmlNode::NodeCase::kElement: {
+ std::unique_ptr<xml::Element> child_el = util::make_unique<xml::Element>();
+ if (!DeserializeXmlFromPb(pb_child, child_el.get(), value_pool, out_error)) {
+ return false;
+ }
+ out_el->AppendChild(std::move(child_el));
+ } break;
+
+ default:
+ LOG(FATAL) << "unknown XmlNode " << (int)pb_child.node_case();
+ break;
+ }
+ }
+ return true;
+}
+
+CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size)
+ : in_(static_cast<const uint8_t*>(data), size) {
+}
+
+void CompiledFileInputStream::EnsureAlignedRead() {
+ const int overflow = in_.CurrentPosition() % 4;
+ if (overflow > 0) {
+ // Reads are always 4 byte aligned.
+ in_.Skip(4 - overflow);
+ }
+}
+
+bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* out_val) {
+ EnsureAlignedRead();
+ return in_.ReadLittleEndian32(out_val);
+}
+
+bool CompiledFileInputStream::ReadCompiledFile(pb::internal::CompiledFile* out_val) {
+ EnsureAlignedRead();
+
+ google::protobuf::uint64 pb_size = 0u;
+ if (!in_.ReadLittleEndian64(&pb_size)) {
+ return false;
+ }
+
+ CodedInputStream::Limit l = in_.PushLimit(static_cast<int>(pb_size));
+
+ // Check that we haven't tried to read past the end.
+ if (static_cast<uint64_t>(in_.BytesUntilLimit()) != pb_size) {
+ in_.PopLimit(l);
+ in_.PushLimit(0);
+ return false;
+ }
+
+ if (!out_val->ParsePartialFromCodedStream(&in_)) {
+ in_.PopLimit(l);
+ in_.PushLimit(0);
+ return false;
+ }
+
+ in_.PopLimit(l);
+ return true;
+}
+
+bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset, uint64_t* out_len) {
+ EnsureAlignedRead();
+
+ google::protobuf::uint64 pb_size = 0u;
+ if (!in_.ReadLittleEndian64(&pb_size)) {
+ return false;
+ }
+
+ // Check that we aren't trying to read past the end.
+ if (pb_size > static_cast<uint64_t>(in_.BytesUntilLimit())) {
+ in_.PushLimit(0);
+ return false;
+ }
+
+ uint64_t offset = static_cast<uint64_t>(in_.CurrentPosition());
+ if (!in_.Skip(pb_size)) {
+ return false;
+ }
+
+ *out_offset = offset;
+ *out_len = pb_size;
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.h b/tools/aapt2/format/proto/ProtoDeserialize.h
new file mode 100644
index 000000000000..c8a7199eb8b5
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoDeserialize.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_FORMAT_PROTO_PROTODESERIALIZE_H
+#define AAPT_FORMAT_PROTO_PROTODESERIALIZE_H
+
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "google/protobuf/io/coded_stream.h"
+
+#include "ConfigDescription.h"
+#include "Configuration.pb.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Resources.pb.h"
+#include "ResourcesInternal.pb.h"
+#include "StringPool.h"
+#include "xml/XmlDom.h"
+
+namespace aapt {
+
+std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config,
+ StringPool* value_pool, std::string* out_error);
+
+std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config, StringPool* value_pool,
+ std::string* out_error);
+
+std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node,
+ std::string* out_error);
+
+bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, StringPool* value_pool,
+ std::string* out_error);
+
+bool DeserializeConfigFromPb(const pb::Configuration& pb_config, ConfigDescription* out_config,
+ std::string* out_error);
+
+bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* out_table,
+ std::string* out_error);
+
+bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file,
+ ResourceFile* out_file, std::string* out_error);
+
+class CompiledFileInputStream {
+ public:
+ explicit CompiledFileInputStream(const void* data, size_t size);
+
+ bool ReadLittleEndian32(uint32_t* outVal);
+ bool ReadCompiledFile(pb::internal::CompiledFile* outVal);
+ bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
+
+ void EnsureAlignedRead();
+
+ ::google::protobuf::io::CodedInputStream in_;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_FORMAT_PROTO_PROTODESERIALIZE_H */
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
new file mode 100644
index 000000000000..c0d36141d457
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2016 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 "format/proto/ProtoSerialize.h"
+
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+
+#include "ValueVisitor.h"
+#include "util/BigBuffer.h"
+
+using ::google::protobuf::io::CodedOutputStream;
+using ::google::protobuf::io::ZeroCopyOutputStream;
+
+namespace aapt {
+
+void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool) {
+ BigBuffer buffer(1024);
+ StringPool::FlattenUtf8(&buffer, pool);
+
+ std::string* data = out_pb_pool->mutable_data();
+ data->reserve(buffer.size());
+
+ size_t offset = 0;
+ for (const BigBuffer::Block& block : buffer) {
+ data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
+ offset += block.size;
+ }
+}
+
+void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source) {
+ StringPool::Ref ref = src_pool->MakeRef(source.path);
+ out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index()));
+ if (source.line) {
+ out_pb_source->mutable_position()->set_line_number(static_cast<uint32_t>(source.line.value()));
+ }
+}
+
+static pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state) {
+ switch (state) {
+ case SymbolState::kPrivate:
+ return pb::SymbolStatus_Visibility_PRIVATE;
+ case SymbolState::kPublic:
+ return pb::SymbolStatus_Visibility_PUBLIC;
+ default:
+ break;
+ }
+ return pb::SymbolStatus_Visibility_UNKNOWN;
+}
+
+void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config) {
+ out_pb_config->set_mcc(config.mcc);
+ out_pb_config->set_mnc(config.mnc);
+ out_pb_config->set_locale(config.GetBcp47LanguageTag());
+
+ switch (config.screenLayout & ConfigDescription::MASK_LAYOUTDIR) {
+ case ConfigDescription::LAYOUTDIR_LTR:
+ out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_LTR);
+ break;
+
+ case ConfigDescription::LAYOUTDIR_RTL:
+ out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_RTL);
+ break;
+ }
+
+ out_pb_config->set_screen_width(config.screenWidth);
+ out_pb_config->set_screen_height(config.screenHeight);
+ out_pb_config->set_screen_width_dp(config.screenWidthDp);
+ out_pb_config->set_screen_height_dp(config.screenHeightDp);
+ out_pb_config->set_smallest_screen_width_dp(config.smallestScreenWidthDp);
+
+ switch (config.screenLayout & ConfigDescription::MASK_SCREENSIZE) {
+ case ConfigDescription::SCREENSIZE_SMALL:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_SMALL);
+ break;
+
+ case ConfigDescription::SCREENSIZE_NORMAL:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_NORMAL);
+ break;
+
+ case ConfigDescription::SCREENSIZE_LARGE:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_LARGE);
+ break;
+
+ case ConfigDescription::SCREENSIZE_XLARGE:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_XLARGE);
+ break;
+ }
+
+ switch (config.screenLayout & ConfigDescription::MASK_SCREENLONG) {
+ case ConfigDescription::SCREENLONG_YES:
+ out_pb_config->set_screen_layout_long(
+ pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_LONG);
+ break;
+
+ case ConfigDescription::SCREENLONG_NO:
+ out_pb_config->set_screen_layout_long(
+ pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_NOTLONG);
+ break;
+ }
+
+ switch (config.screenLayout2 & ConfigDescription::MASK_SCREENROUND) {
+ case ConfigDescription::SCREENROUND_YES:
+ out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_ROUND);
+ break;
+
+ case ConfigDescription::SCREENROUND_NO:
+ out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_NOTROUND);
+ break;
+ }
+
+ switch (config.colorMode & ConfigDescription::MASK_WIDE_COLOR_GAMUT) {
+ case ConfigDescription::WIDE_COLOR_GAMUT_YES:
+ out_pb_config->set_wide_color_gamut(pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_WIDECG);
+ break;
+
+ case ConfigDescription::WIDE_COLOR_GAMUT_NO:
+ out_pb_config->set_wide_color_gamut(
+ pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_NOWIDECG);
+ break;
+ }
+
+ switch (config.colorMode & ConfigDescription::MASK_HDR) {
+ case ConfigDescription::HDR_YES:
+ out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_HIGHDR);
+ break;
+
+ case ConfigDescription::HDR_NO:
+ out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_LOWDR);
+ break;
+ }
+
+ switch (config.orientation) {
+ case ConfigDescription::ORIENTATION_PORT:
+ out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_PORT);
+ break;
+
+ case ConfigDescription::ORIENTATION_LAND:
+ out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_LAND);
+ break;
+
+ case ConfigDescription::ORIENTATION_SQUARE:
+ out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_SQUARE);
+ break;
+ }
+
+ switch (config.uiMode & ConfigDescription::MASK_UI_MODE_TYPE) {
+ case ConfigDescription::UI_MODE_TYPE_NORMAL:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_NORMAL);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_DESK:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_DESK);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_CAR:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_CAR);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_TELEVISION:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_TELEVISION);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_APPLIANCE:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_APPLIANCE);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_WATCH:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_WATCH);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_VR_HEADSET:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_VRHEADSET);
+ break;
+ }
+
+ switch (config.uiMode & ConfigDescription::MASK_UI_MODE_NIGHT) {
+ case ConfigDescription::UI_MODE_NIGHT_YES:
+ out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NIGHT);
+ break;
+
+ case ConfigDescription::UI_MODE_NIGHT_NO:
+ out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NOTNIGHT);
+ break;
+ }
+
+ out_pb_config->set_density(config.density);
+
+ switch (config.touchscreen) {
+ case ConfigDescription::TOUCHSCREEN_NOTOUCH:
+ out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_NOTOUCH);
+ break;
+
+ case ConfigDescription::TOUCHSCREEN_STYLUS:
+ out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_STYLUS);
+ break;
+
+ case ConfigDescription::TOUCHSCREEN_FINGER:
+ out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_FINGER);
+ break;
+ }
+
+ switch (config.inputFlags & ConfigDescription::MASK_KEYSHIDDEN) {
+ case ConfigDescription::KEYSHIDDEN_NO:
+ out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSEXPOSED);
+ break;
+
+ case ConfigDescription::KEYSHIDDEN_YES:
+ out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSHIDDEN);
+ break;
+
+ case ConfigDescription::KEYSHIDDEN_SOFT:
+ out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSSOFT);
+ break;
+ }
+
+ switch (config.keyboard) {
+ case ConfigDescription::KEYBOARD_NOKEYS:
+ out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_NOKEYS);
+ break;
+
+ case ConfigDescription::KEYBOARD_QWERTY:
+ out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_QWERTY);
+ break;
+
+ case ConfigDescription::KEYBOARD_12KEY:
+ out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_TWELVEKEY);
+ break;
+ }
+
+ switch (config.inputFlags & ConfigDescription::MASK_NAVHIDDEN) {
+ case ConfigDescription::NAVHIDDEN_NO:
+ out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVEXPOSED);
+ break;
+
+ case ConfigDescription::NAVHIDDEN_YES:
+ out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVHIDDEN);
+ break;
+ }
+
+ switch (config.navigation) {
+ case ConfigDescription::NAVIGATION_NONAV:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_NONAV);
+ break;
+
+ case ConfigDescription::NAVIGATION_DPAD:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_DPAD);
+ break;
+
+ case ConfigDescription::NAVIGATION_TRACKBALL:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_TRACKBALL);
+ break;
+
+ case ConfigDescription::NAVIGATION_WHEEL:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_WHEEL);
+ break;
+ }
+
+ out_pb_config->set_sdk_version(config.sdkVersion);
+}
+
+void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table) {
+ StringPool source_pool;
+ for (const std::unique_ptr<ResourceTablePackage>& package : table.packages) {
+ pb::Package* pb_package = out_table->add_package();
+ if (package->id) {
+ pb_package->mutable_package_id()->set_id(package->id.value());
+ }
+ pb_package->set_package_name(package->name);
+
+ for (const std::unique_ptr<ResourceTableType>& type : package->types) {
+ pb::Type* pb_type = pb_package->add_type();
+ if (type->id) {
+ pb_type->mutable_type_id()->set_id(type->id.value());
+ }
+ pb_type->set_name(ToString(type->type).to_string());
+
+ for (const std::unique_ptr<ResourceEntry>& entry : type->entries) {
+ pb::Entry* pb_entry = pb_type->add_entry();
+ if (entry->id) {
+ pb_entry->mutable_entry_id()->set_id(entry->id.value());
+ }
+ pb_entry->set_name(entry->name);
+
+ // Write the SymbolStatus struct.
+ pb::SymbolStatus* pb_status = pb_entry->mutable_symbol_status();
+ pb_status->set_visibility(SerializeVisibilityToPb(entry->symbol_status.state));
+ SerializeSourceToPb(entry->symbol_status.source, &source_pool, pb_status->mutable_source());
+ pb_status->set_comment(entry->symbol_status.comment);
+ pb_status->set_allow_new(entry->symbol_status.allow_new);
+
+ for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
+ pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
+ SerializeConfig(config_value->config, pb_config_value->mutable_config());
+ pb_config_value->mutable_config()->set_product(config_value->product);
+ SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(), &source_pool);
+ }
+ }
+ }
+ }
+ SerializeStringPoolToPb(source_pool, out_table->mutable_source_pool());
+}
+
+static pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
+ switch (type) {
+ case Reference::Type::kResource:
+ return pb::Reference_Type_REFERENCE;
+ case Reference::Type::kAttribute:
+ return pb::Reference_Type_ATTRIBUTE;
+ default:
+ break;
+ }
+ return pb::Reference_Type_REFERENCE;
+}
+
+static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
+ pb_ref->set_id(ref.id.value_or_default(ResourceId(0x0)).id);
+
+ if (ref.name) {
+ pb_ref->set_name(ref.name.value().ToString());
+ }
+
+ pb_ref->set_private_(ref.private_reference);
+ pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
+}
+
+template <typename T>
+static void SerializeItemMetaDataToPb(const Item& item, T* pb_item, StringPool* src_pool) {
+ if (src_pool != nullptr) {
+ SerializeSourceToPb(item.GetSource(), src_pool, pb_item->mutable_source());
+ }
+ pb_item->set_comment(item.GetComment());
+}
+
+static pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
+ switch (plural_idx) {
+ case Plural::Zero:
+ return pb::Plural_Arity_ZERO;
+ case Plural::One:
+ return pb::Plural_Arity_ONE;
+ case Plural::Two:
+ return pb::Plural_Arity_TWO;
+ case Plural::Few:
+ return pb::Plural_Arity_FEW;
+ case Plural::Many:
+ return pb::Plural_Arity_MANY;
+ default:
+ break;
+ }
+ return pb::Plural_Arity_OTHER;
+}
+
+namespace {
+
+class ValueSerializer : public ConstValueVisitor {
+ public:
+ using ConstValueVisitor::Visit;
+
+ ValueSerializer(pb::Value* out_value, StringPool* src_pool)
+ : out_value_(out_value), src_pool_(src_pool) {
+ }
+
+ void Visit(const Reference* ref) override {
+ SerializeReferenceToPb(*ref, out_value_->mutable_item()->mutable_ref());
+ }
+
+ void Visit(const String* str) override {
+ out_value_->mutable_item()->mutable_str()->set_value(*str->value);
+ }
+
+ void Visit(const RawString* str) override {
+ out_value_->mutable_item()->mutable_raw_str()->set_value(*str->value);
+ }
+
+ void Visit(const StyledString* str) override {
+ pb::StyledString* pb_str = out_value_->mutable_item()->mutable_styled_str();
+ pb_str->set_value(str->value->value);
+ for (const StringPool::Span& span : str->value->spans) {
+ pb::StyledString::Span* pb_span = pb_str->add_span();
+ pb_span->set_tag(*span.name);
+ pb_span->set_first_char(span.first_char);
+ pb_span->set_last_char(span.last_char);
+ }
+ }
+
+ void Visit(const FileReference* file) override {
+ out_value_->mutable_item()->mutable_file()->set_path(*file->path);
+ }
+
+ void Visit(const Id* /*id*/) override {
+ out_value_->mutable_item()->mutable_id();
+ }
+
+ void Visit(const BinaryPrimitive* prim) override {
+ android::Res_value val = {};
+ prim->Flatten(&val);
+
+ pb::Primitive* pb_prim = out_value_->mutable_item()->mutable_prim();
+ pb_prim->set_type(val.dataType);
+ pb_prim->set_data(val.data);
+ }
+
+ void Visit(const Attribute* attr) override {
+ pb::Attribute* pb_attr = out_value_->mutable_compound_value()->mutable_attr();
+ pb_attr->set_format_flags(attr->type_mask);
+ pb_attr->set_min_int(attr->min_int);
+ pb_attr->set_max_int(attr->max_int);
+
+ for (auto& symbol : attr->symbols) {
+ pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbol();
+ SerializeItemMetaDataToPb(symbol.symbol, pb_symbol, src_pool_);
+ SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
+ pb_symbol->set_value(symbol.value);
+ }
+ }
+
+ void Visit(const Style* style) override {
+ pb::Style* pb_style = out_value_->mutable_compound_value()->mutable_style();
+ if (style->parent) {
+ const Reference& parent = style->parent.value();
+ SerializeReferenceToPb(parent, pb_style->mutable_parent());
+ if (src_pool_ != nullptr) {
+ SerializeSourceToPb(parent.GetSource(), src_pool_, pb_style->mutable_parent_source());
+ }
+ }
+
+ for (const Style::Entry& entry : style->entries) {
+ pb::Style_Entry* pb_entry = pb_style->add_entry();
+ SerializeReferenceToPb(entry.key, pb_entry->mutable_key());
+ SerializeItemMetaDataToPb(entry.key, pb_entry, src_pool_);
+ SerializeItemToPb(*entry.value, pb_entry->mutable_item());
+ }
+ }
+
+ void Visit(const Styleable* styleable) override {
+ pb::Styleable* pb_styleable = out_value_->mutable_compound_value()->mutable_styleable();
+ for (const Reference& entry : styleable->entries) {
+ pb::Styleable_Entry* pb_entry = pb_styleable->add_entry();
+ SerializeItemMetaDataToPb(entry, pb_entry, src_pool_);
+ SerializeReferenceToPb(entry, pb_entry->mutable_attr());
+ }
+ }
+
+ void Visit(const Array* array) override {
+ pb::Array* pb_array = out_value_->mutable_compound_value()->mutable_array();
+ for (const std::unique_ptr<Item>& element : array->elements) {
+ pb::Array_Element* pb_element = pb_array->add_element();
+ SerializeItemMetaDataToPb(*element, pb_element, src_pool_);
+ SerializeItemToPb(*element, pb_element->mutable_item());
+ }
+ }
+
+ void Visit(const Plural* plural) override {
+ pb::Plural* pb_plural = out_value_->mutable_compound_value()->mutable_plural();
+ const size_t count = plural->values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (!plural->values[i]) {
+ // No plural value set here.
+ continue;
+ }
+
+ pb::Plural_Entry* pb_entry = pb_plural->add_entry();
+ pb_entry->set_arity(SerializePluralEnumToPb(i));
+ SerializeItemMetaDataToPb(*plural->values[i], pb_entry, src_pool_);
+ SerializeItemToPb(*plural->values[i], pb_entry->mutable_item());
+ }
+ }
+
+ void VisitAny(const Value* unknown) override {
+ LOG(FATAL) << "unimplemented value: " << *unknown;
+ }
+
+ private:
+ pb::Value* out_value_;
+ StringPool* src_pool_;
+};
+
+} // namespace
+
+void SerializeValueToPb(const Value& value, pb::Value* out_value, StringPool* src_pool) {
+ ValueSerializer serializer(out_value, src_pool);
+ value.Accept(&serializer);
+
+ // Serialize the meta-data of the Value.
+ out_value->set_comment(value.GetComment());
+ out_value->set_weak(value.IsWeak());
+ if (src_pool != nullptr) {
+ SerializeSourceToPb(value.GetSource(), src_pool, out_value->mutable_source());
+ }
+}
+
+void SerializeItemToPb(const Item& item, pb::Item* out_item) {
+ pb::Value value;
+ ValueSerializer serializer(&value, nullptr);
+ item.Accept(&serializer);
+ out_item->MergeFrom(value.item());
+}
+
+void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
+ out_file->set_resource_name(file.name.ToString());
+ out_file->set_source_path(file.source.path);
+ SerializeConfig(file.config, out_file->mutable_config());
+
+ for (const SourcedResourceName& exported : file.exported_symbols) {
+ pb::internal::CompiledFile_Symbol* pb_symbol = out_file->add_exported_symbol();
+ pb_symbol->set_resource_name(exported.name.ToString());
+ pb_symbol->mutable_source()->set_line_number(exported.line);
+ }
+}
+
+static void SerializeXmlCommon(const xml::Node& node, pb::XmlNode* out_node) {
+ pb::SourcePosition* pb_src = out_node->mutable_source();
+ pb_src->set_line_number(node.line_number);
+ pb_src->set_column_number(node.column_number);
+}
+
+void SerializeXmlToPb(const xml::Element& el, pb::XmlNode* out_node) {
+ SerializeXmlCommon(el, out_node);
+
+ pb::XmlElement* pb_element = out_node->mutable_element();
+ pb_element->set_name(el.name);
+ pb_element->set_namespace_uri(el.namespace_uri);
+
+ for (const xml::NamespaceDecl& ns : el.namespace_decls) {
+ pb::XmlNamespace* pb_ns = pb_element->add_namespace_declaration();
+ pb_ns->set_prefix(ns.prefix);
+ pb_ns->set_uri(ns.uri);
+ pb::SourcePosition* pb_src = pb_ns->mutable_source();
+ pb_src->set_line_number(ns.line_number);
+ pb_src->set_column_number(ns.column_number);
+ }
+
+ for (const xml::Attribute& attr : el.attributes) {
+ pb::XmlAttribute* pb_attr = pb_element->add_attribute();
+ pb_attr->set_name(attr.name);
+ pb_attr->set_namespace_uri(attr.namespace_uri);
+ pb_attr->set_value(attr.value);
+ if (attr.compiled_attribute) {
+ const ResourceId attr_id = attr.compiled_attribute.value().id.value_or_default({});
+ pb_attr->set_resource_id(attr_id.id);
+ }
+ if (attr.compiled_value != nullptr) {
+ SerializeItemToPb(*attr.compiled_value, pb_attr->mutable_compiled_item());
+ pb::SourcePosition* pb_src = pb_attr->mutable_source();
+ pb_src->set_line_number(attr.compiled_value->GetSource().line.value_or_default(0));
+ }
+ }
+
+ for (const std::unique_ptr<xml::Node>& child : el.children) {
+ if (const xml::Element* child_el = xml::NodeCast<xml::Element>(child.get())) {
+ SerializeXmlToPb(*child_el, pb_element->add_child());
+ } else if (const xml::Text* text_el = xml::NodeCast<xml::Text>(child.get())) {
+ pb::XmlNode* pb_child_node = pb_element->add_child();
+ SerializeXmlCommon(*text_el, pb_child_node);
+ pb_child_node->set_text(text_el->text);
+ } else {
+ LOG(FATAL) << "unhandled XmlNode type";
+ }
+ }
+}
+
+void SerializeXmlResourceToPb(const xml::XmlResource& resource, pb::XmlNode* out_node) {
+ SerializeXmlToPb(*resource.root, out_node);
+}
+
+CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : out_(out) {
+}
+
+void CompiledFileOutputStream::EnsureAlignedWrite() {
+ const int overflow = out_.ByteCount() % 4;
+ if (overflow > 0) {
+ uint32_t zero = 0u;
+ out_.WriteRaw(&zero, 4 - overflow);
+ }
+}
+
+void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) {
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian32(val);
+}
+
+void CompiledFileOutputStream::WriteCompiledFile(const pb::internal::CompiledFile& compiled_file) {
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian64(static_cast<uint64_t>(compiled_file.ByteSize()));
+ compiled_file.SerializeWithCachedSizes(&out_);
+}
+
+void CompiledFileOutputStream::WriteData(const BigBuffer& buffer) {
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian64(static_cast<uint64_t>(buffer.size()));
+ for (const BigBuffer::Block& block : buffer) {
+ out_.WriteRaw(block.buffer.get(), block.size);
+ }
+}
+
+void CompiledFileOutputStream::WriteData(const void* data, size_t len) {
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian64(static_cast<uint64_t>(len));
+ out_.WriteRaw(data, len);
+}
+
+bool CompiledFileOutputStream::HadError() {
+ return out_.HadError();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoSerialize.h b/tools/aapt2/format/proto/ProtoSerialize.h
new file mode 100644
index 000000000000..1694b16f3277
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoSerialize.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 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_FORMAT_PROTO_PROTOSERIALIZE_H
+#define AAPT_FORMAT_PROTO_PROTOSERIALIZE_H
+
+#include "android-base/macros.h"
+#include "google/protobuf/io/coded_stream.h"
+
+#include "ConfigDescription.h"
+#include "Configuration.pb.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Resources.pb.h"
+#include "ResourcesInternal.pb.h"
+#include "StringPool.h"
+#include "xml/XmlDom.h"
+
+namespace google {
+namespace protobuf {
+namespace io {
+class ZeroCopyOutputStream;
+} // namespace io
+} // namespace protobuf
+} // namespace google
+
+namespace aapt {
+
+// Serializes a Value to its protobuf representation. An optional StringPool will hold the
+// source path string.
+void SerializeValueToPb(const Value& value, pb::Value* out_value, StringPool* src_pool = nullptr);
+
+// Serialize an Item into its protobuf representation. pb::Item does not store the source path nor
+// comments of an Item.
+void SerializeItemToPb(const Item& item, pb::Item* out_item);
+
+// Serializes an XML element into its protobuf representation.
+void SerializeXmlToPb(const xml::Element& el, pb::XmlNode* out_node);
+
+// Serializes an XmlResource into its protobuf representation. The ResourceFile is NOT serialized.
+void SerializeXmlResourceToPb(const xml::XmlResource& resource, pb::XmlNode* out_node);
+
+// Serializes a StringPool into its protobuf representation, which is really just the binary
+// ResStringPool representation stuffed into a bytes field.
+void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool);
+
+// Serializes a ConfigDescription into its protobuf representation.
+void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config);
+
+// Serializes a ResourceTable into its protobuf representation.
+void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table);
+
+// Serializes a ResourceFile into its protobuf representation.
+void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file);
+
+class CompiledFileOutputStream {
+ public:
+ explicit CompiledFileOutputStream(::google::protobuf::io::ZeroCopyOutputStream* out);
+
+ void WriteLittleEndian32(uint32_t value);
+ void WriteCompiledFile(const pb::internal::CompiledFile& compiledFile);
+ void WriteData(const BigBuffer& buffer);
+ void WriteData(const void* data, size_t len);
+ bool HadError();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
+
+ void EnsureAlignedWrite();
+
+ ::google::protobuf::io::CodedOutputStream out_;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_FORMAT_PROTO_PROTOSERIALIZE_H */
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
new file mode 100644
index 000000000000..2154d5a9ab2c
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2016 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 "format/proto/ProtoSerialize.h"
+
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+
+#include "ResourceUtils.h"
+#include "format/proto/ProtoDeserialize.h"
+#include "test/Test.h"
+
+using ::android::StringPiece;
+using ::google::protobuf::io::StringOutputStream;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::NotNull;
+using ::testing::SizeIs;
+using ::testing::StrEq;
+
+namespace aapt {
+
+TEST(ProtoSerializeTest, SerializeSinglePackage) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000), "res/layout/main.xml")
+ .AddReference("com.app.a:layout/other", ResourceId(0x7f020001), "com.app.a:layout/main")
+ .AddString("com.app.a:string/text", {}, "hi")
+ .AddValue("com.app.a:id/foo", {}, util::make_unique<Id>())
+ .SetSymbolState("com.app.a:bool/foo", {}, SymbolState::kUndefined, true /*allow_new*/)
+ .Build();
+
+ Symbol public_symbol;
+ public_symbol.state = SymbolState::kPublic;
+ ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"),
+ ResourceId(0x7f020000), public_symbol,
+ context->GetDiagnostics()));
+
+ Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
+ ASSERT_THAT(id, NotNull());
+
+ // Make a plural.
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ plural->values[Plural::One] = util::make_unique<String>(table->string_pool.MakeRef("one"));
+ ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:plurals/hey"), ConfigDescription{},
+ {}, std::move(plural), context->GetDiagnostics()));
+
+ // Make a styled string.
+ StyleString style_string;
+ style_string.str = "hello";
+ style_string.spans.push_back(Span{"b", 0u, 4u});
+ ASSERT_TRUE(
+ table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {},
+ util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)),
+ context->GetDiagnostics()));
+
+ // Make a resource with different products.
+ ASSERT_TRUE(table->AddResource(
+ test::ParseNameOrDie("com.app.a:integer/one"), test::ParseConfigOrDie("land"), {},
+ test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), context->GetDiagnostics()));
+ ASSERT_TRUE(table->AddResource(
+ test::ParseNameOrDie("com.app.a:integer/one"), test::ParseConfigOrDie("land"), "tablet",
+ test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), context->GetDiagnostics()));
+
+ // Make a reference with both resource name and resource ID.
+ // The reference should point to a resource outside of this table to test that both name and id
+ // get serialized.
+ Reference expected_ref;
+ expected_ref.name = test::ParseNameOrDie("android:layout/main");
+ expected_ref.id = ResourceId(0x01020000);
+ ASSERT_TRUE(table->AddResource(
+ test::ParseNameOrDie("com.app.a:layout/abc"), ConfigDescription::DefaultConfig(), {},
+ util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
+
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(*table, &pb_table);
+
+ ResourceTable new_table;
+ std::string error;
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ Id* new_id = test::GetValue<Id>(&new_table, "com.app.a:id/foo");
+ ASSERT_THAT(new_id, NotNull());
+ EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
+
+ Maybe<ResourceTable::SearchResult> result =
+ new_table.FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
+ ASSERT_TRUE(result);
+
+ EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic));
+ EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
+
+ result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
+ ASSERT_TRUE(result);
+ EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined));
+ EXPECT_TRUE(result.value().entry->symbol_status.allow_new);
+
+ // Find the product-dependent values
+ BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
+ &new_table, "com.app.a:integer/one", test::ParseConfigOrDie("land"), "");
+ ASSERT_THAT(prim, NotNull());
+ EXPECT_THAT(prim->value.data, Eq(123u));
+
+ prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
+ &new_table, "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
+ ASSERT_THAT(prim, NotNull());
+ EXPECT_THAT(prim->value.data, Eq(321u));
+
+ Reference* actual_ref = test::GetValue<Reference>(&new_table, "com.app.a:layout/abc");
+ ASSERT_THAT(actual_ref, NotNull());
+ ASSERT_TRUE(actual_ref->name);
+ ASSERT_TRUE(actual_ref->id);
+ EXPECT_THAT(*actual_ref, Eq(expected_ref));
+
+ StyledString* actual_styled_str =
+ test::GetValue<StyledString>(&new_table, "com.app.a:string/styled");
+ ASSERT_THAT(actual_styled_str, NotNull());
+ EXPECT_THAT(actual_styled_str->value->value, Eq("hello"));
+ ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u));
+ EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b"));
+ EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
+ EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
+}
+
+TEST(ProtoSerializeTest, SerializeFileHeader) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ ResourceFile f;
+ f.config = test::ParseConfigOrDie("hdpi-v9");
+ f.name = test::ParseNameOrDie("com.app.a:layout/main");
+ f.source.path = "res/layout-hdpi-v9/main.xml";
+ f.exported_symbols.push_back(SourcedResourceName{test::ParseNameOrDie("id/unchecked"), 23u});
+
+ const std::string expected_data1 = "123";
+ const std::string expected_data2 = "1234";
+
+ std::string output_str;
+ {
+ pb::internal::CompiledFile pb_f1, pb_f2;
+ SerializeCompiledFileToPb(f, &pb_f1);
+
+ f.name.entry = "__" + f.name.entry + "$0";
+ SerializeCompiledFileToPb(f, &pb_f2);
+
+ StringOutputStream out_stream(&output_str);
+ CompiledFileOutputStream out_file_stream(&out_stream);
+ out_file_stream.WriteLittleEndian32(2);
+ out_file_stream.WriteCompiledFile(pb_f1);
+ out_file_stream.WriteData(expected_data1.data(), expected_data1.size());
+ out_file_stream.WriteCompiledFile(pb_f2);
+ out_file_stream.WriteData(expected_data2.data(), expected_data2.size());
+ ASSERT_FALSE(out_file_stream.HadError());
+ }
+
+ CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
+ uint32_t num_files = 0;
+ ASSERT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
+ ASSERT_EQ(2u, num_files);
+
+ // Read the first compiled file.
+
+ pb::internal::CompiledFile new_pb_f1;
+ ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_f1));
+
+ ResourceFile new_f1;
+ std::string error;
+ ASSERT_TRUE(DeserializeCompiledFileFromPb(new_pb_f1, &new_f1, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ uint64_t offset, len;
+ ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
+
+ std::string actual_data(output_str.data() + offset, len);
+ EXPECT_EQ(expected_data1, actual_data);
+
+ // Expect the data to be aligned.
+ EXPECT_EQ(0u, offset & 0x03);
+
+ ASSERT_EQ(1u, new_f1.exported_symbols.size());
+ EXPECT_EQ(test::ParseNameOrDie("id/unchecked"), new_f1.exported_symbols[0].name);
+
+ // Read the second compiled file.
+
+ pb::internal::CompiledFile new_pb_f2;
+ ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_f2));
+
+ ResourceFile new_f2;
+ ASSERT_TRUE(DeserializeCompiledFileFromPb(new_pb_f2, &new_f2, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
+
+ actual_data = std::string(output_str.data() + offset, len);
+ EXPECT_EQ(expected_data2, actual_data);
+
+ // Expect the data to be aligned.
+ EXPECT_EQ(0u, offset & 0x03);
+}
+
+TEST(ProtoSerializeTest, DeserializeCorruptHeaderSafely) {
+ ResourceFile f;
+ pb::internal::CompiledFile pb_file;
+ SerializeCompiledFileToPb(f, &pb_file);
+
+ const std::string expected_data = "1234";
+
+ std::string output_str;
+ {
+ StringOutputStream out_stream(&output_str);
+ CompiledFileOutputStream out_file_stream(&out_stream);
+ out_file_stream.WriteLittleEndian32(1);
+ out_file_stream.WriteCompiledFile(pb_file);
+ out_file_stream.WriteData(expected_data.data(), expected_data.size());
+ ASSERT_FALSE(out_file_stream.HadError());
+ }
+
+ output_str[4] = 0xff;
+
+ CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
+
+ uint32_t num_files = 0;
+ EXPECT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
+ EXPECT_EQ(1u, num_files);
+
+ pb::internal::CompiledFile new_pb_file;
+ EXPECT_FALSE(in_file_stream.ReadCompiledFile(&new_pb_file));
+
+ uint64_t offset, len;
+ EXPECT_FALSE(in_file_stream.ReadDataMetaData(&offset, &len));
+}
+
+TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
+ xml::Element element;
+ element.line_number = 22;
+ element.column_number = 23;
+ element.name = "element";
+ element.namespace_uri = "uri://";
+
+ xml::NamespaceDecl decl;
+ decl.prefix = "android";
+ decl.uri = xml::kSchemaAndroid;
+ decl.line_number = 21;
+ decl.column_number = 24;
+
+ element.namespace_decls.push_back(decl);
+
+ xml::Attribute attr;
+ attr.name = "name";
+ attr.namespace_uri = xml::kSchemaAndroid;
+ attr.value = "23dp";
+ attr.compiled_attribute = xml::AaptAttribute({}, ResourceId(0x01010000));
+ attr.compiled_value =
+ ResourceUtils::TryParseItemForAttribute(attr.value, android::ResTable_map::TYPE_DIMENSION);
+ attr.compiled_value->SetSource(Source().WithLine(25));
+ element.attributes.push_back(std::move(attr));
+
+ std::unique_ptr<xml::Text> text = util::make_unique<xml::Text>();
+ text->line_number = 25;
+ text->column_number = 3;
+ text->text = "hey there";
+ element.AppendChild(std::move(text));
+
+ std::unique_ptr<xml::Element> child = util::make_unique<xml::Element>();
+ child->name = "child";
+
+ text = util::make_unique<xml::Text>();
+ text->text = "woah there";
+ child->AppendChild(std::move(text));
+
+ element.AppendChild(std::move(child));
+
+ pb::XmlNode pb_xml;
+ SerializeXmlToPb(element, &pb_xml);
+
+ StringPool pool;
+ xml::Element actual_el;
+ std::string error;
+ ASSERT_TRUE(DeserializeXmlFromPb(pb_xml, &actual_el, &pool, &error));
+ ASSERT_THAT(error, IsEmpty());
+
+ EXPECT_THAT(actual_el.name, StrEq("element"));
+ EXPECT_THAT(actual_el.namespace_uri, StrEq("uri://"));
+ EXPECT_THAT(actual_el.line_number, Eq(22u));
+ EXPECT_THAT(actual_el.column_number, Eq(23u));
+
+ ASSERT_THAT(actual_el.namespace_decls, SizeIs(1u));
+ const xml::NamespaceDecl& actual_decl = actual_el.namespace_decls[0];
+ EXPECT_THAT(actual_decl.prefix, StrEq("android"));
+ EXPECT_THAT(actual_decl.uri, StrEq(xml::kSchemaAndroid));
+ EXPECT_THAT(actual_decl.line_number, Eq(21u));
+ EXPECT_THAT(actual_decl.column_number, Eq(24u));
+
+ ASSERT_THAT(actual_el.attributes, SizeIs(1u));
+ const xml::Attribute& actual_attr = actual_el.attributes[0];
+ EXPECT_THAT(actual_attr.name, StrEq("name"));
+ EXPECT_THAT(actual_attr.namespace_uri, StrEq(xml::kSchemaAndroid));
+ EXPECT_THAT(actual_attr.value, StrEq("23dp"));
+
+ ASSERT_THAT(actual_attr.compiled_value, NotNull());
+ const BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(actual_attr.compiled_value.get());
+ ASSERT_THAT(prim, NotNull());
+ EXPECT_THAT(prim->value.dataType, Eq(android::Res_value::TYPE_DIMENSION));
+
+ ASSERT_TRUE(actual_attr.compiled_attribute);
+ ASSERT_TRUE(actual_attr.compiled_attribute.value().id);
+
+ ASSERT_THAT(actual_el.children, SizeIs(2u));
+ const xml::Text* child_text = xml::NodeCast<xml::Text>(actual_el.children[0].get());
+ ASSERT_THAT(child_text, NotNull());
+ const xml::Element* child_el = xml::NodeCast<xml::Element>(actual_el.children[1].get());
+ ASSERT_THAT(child_el, NotNull());
+
+ EXPECT_THAT(child_text->line_number, Eq(25u));
+ EXPECT_THAT(child_text->column_number, Eq(3u));
+ EXPECT_THAT(child_text->text, StrEq("hey there"));
+
+ EXPECT_THAT(child_el->name, StrEq("child"));
+ ASSERT_THAT(child_el->children, SizeIs(1u));
+
+ child_text = xml::NodeCast<xml::Text>(child_el->children[0].get());
+ ASSERT_THAT(child_text, NotNull());
+ EXPECT_THAT(child_text->text, StrEq("woah there"));
+}
+
+static void ExpectConfigSerializes(const StringPiece& config_str) {
+ const ConfigDescription expected_config = test::ParseConfigOrDie(config_str);
+ pb::Configuration pb_config;
+ SerializeConfig(expected_config, &pb_config);
+
+ ConfigDescription actual_config;
+ std::string error;
+ ASSERT_TRUE(DeserializeConfigFromPb(pb_config, &actual_config, &error));
+ ASSERT_THAT(error, IsEmpty());
+ EXPECT_EQ(expected_config, actual_config);
+}
+
+TEST(ProtoSerializeTest, SerializeDeserializeConfiguration) {
+ ExpectConfigSerializes("");
+
+ ExpectConfigSerializes("mcc123");
+
+ ExpectConfigSerializes("mnc123");
+
+ ExpectConfigSerializes("en");
+ ExpectConfigSerializes("en-rGB");
+ ExpectConfigSerializes("b+en+GB");
+
+ ExpectConfigSerializes("ldltr");
+ ExpectConfigSerializes("ldrtl");
+
+ ExpectConfigSerializes("sw3600dp");
+
+ ExpectConfigSerializes("w300dp");
+
+ ExpectConfigSerializes("h400dp");
+
+ ExpectConfigSerializes("small");
+ ExpectConfigSerializes("normal");
+ ExpectConfigSerializes("large");
+ ExpectConfigSerializes("xlarge");
+
+ ExpectConfigSerializes("long");
+ ExpectConfigSerializes("notlong");
+
+ ExpectConfigSerializes("round");
+ ExpectConfigSerializes("notround");
+
+ ExpectConfigSerializes("widecg");
+ ExpectConfigSerializes("nowidecg");
+
+ ExpectConfigSerializes("highdr");
+ ExpectConfigSerializes("lowdr");
+
+ ExpectConfigSerializes("port");
+ ExpectConfigSerializes("land");
+ ExpectConfigSerializes("square");
+
+ ExpectConfigSerializes("desk");
+ ExpectConfigSerializes("car");
+ ExpectConfigSerializes("television");
+ ExpectConfigSerializes("appliance");
+ ExpectConfigSerializes("watch");
+ ExpectConfigSerializes("vrheadset");
+
+ ExpectConfigSerializes("night");
+ ExpectConfigSerializes("notnight");
+
+ ExpectConfigSerializes("300dpi");
+ ExpectConfigSerializes("hdpi");
+
+ ExpectConfigSerializes("notouch");
+ ExpectConfigSerializes("stylus");
+ ExpectConfigSerializes("finger");
+
+ ExpectConfigSerializes("keysexposed");
+ ExpectConfigSerializes("keyshidden");
+ ExpectConfigSerializes("keyssoft");
+
+ ExpectConfigSerializes("nokeys");
+ ExpectConfigSerializes("qwerty");
+ ExpectConfigSerializes("12key");
+
+ ExpectConfigSerializes("navhidden");
+ ExpectConfigSerializes("navexposed");
+
+ ExpectConfigSerializes("nonav");
+ ExpectConfigSerializes("dpad");
+ ExpectConfigSerializes("trackball");
+ ExpectConfigSerializes("wheel");
+
+ ExpectConfigSerializes("300x200");
+
+ ExpectConfigSerializes("v8");
+
+ ExpectConfigSerializes(
+ "mcc123-mnc456-b+en+GB-ldltr-sw300dp-w300dp-h400dp-large-long-round-widecg-highdr-land-car-"
+ "night-xhdpi-stylus-keysexposed-qwerty-navhidden-dpad-300x200-v23");
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Android.mk
new file mode 100644
index 000000000000..6361f9b8ae7d
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH := $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
new file mode 100644
index 000000000000..6ed07b0c5c73
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
@@ -0,0 +1,29 @@
+#
+# 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_App
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ AaptTestNamespace_LibOne \
+ AaptTestNamespace_LibTwo
+LOCAL_AAPT_FLAGS := -v
+include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/App/AndroidManifest.xml
new file mode 100644
index 000000000000..6398a83ea1d2
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?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.app">
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
+
+ <application android:theme="@style/AppTheme" android:label="@string/app_name">
+ <activity android:name=".MainActivity">
+ <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/App/res/layout/activity_main.xml b/tools/aapt2/integration-tests/NamespaceTest/App/res/layout/activity_main.xml
new file mode 100644
index 000000000000..19bfd942a5bd
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/res/layout/activity_main.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:libone="http://schemas.android.com/apk/res/com.android.aapt.namespace.libone"
+ xmlns:libtwo="http://schemas.android.com/apk/res/com.android.aapt.namespace.libtwo"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <com.android.aapt.namespace.libtwo.TextView
+ android:id="@+id/textview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="@bool/always_true"
+ android:text="@libone:string/textview_text"
+ libtwo:textview_attr="?libone:theme_attr" />
+</RelativeLayout> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
new file mode 100644
index 000000000000..1b80d9542881
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
@@ -0,0 +1,29 @@
+<?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="app_name">Namespace App</string>
+ <bool name="always_true">true</bool>
+
+ <style name="AppTheme" parent="com.android.aapt.namespace.libone:style/Theme">
+ <item name="android:colorPrimary">#3F51B5</item>
+ <item name="android:colorPrimaryDark">#303F9F</item>
+ <item name="android:colorAccent">#FF4081</item>
+ <item name="com.android.aapt.namespace.libone:theme_attr">
+ @com.android.aapt.namespace.libtwo:string/public_string
+ </item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/src/com/android/aapt/namespace/app/MainActivity.java b/tools/aapt2/integration-tests/NamespaceTest/App/src/com/android/aapt/namespace/app/MainActivity.java
new file mode 100644
index 000000000000..fcb4c3c12f81
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/src/com/android/aapt/namespace/app/MainActivity.java
@@ -0,0 +1,34 @@
+/*
+ * 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.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Toast;
+
+public class MainActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ com.android.aapt.namespace.libtwo.TextView tv = findViewById(R.id.textview);
+
+
+
+ Toast.makeText(this, tv.getTextViewAttr(), Toast.LENGTH_LONG).show();
+ }
+}
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
new file mode 100644
index 000000000000..b1cac68dae7a
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/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_MODULE := AaptTestNamespace_LibOne
+LOCAL_MODULE_TAGS := tests
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+# We need this to retain the R.java generated for this library.
+LOCAL_JAR_EXCLUDE_FILES := none
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/LibOne/AndroidManifest.xml
new file mode 100644
index 000000000000..70b4b226624b
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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.libone">
+
+ <uses-sdk android:minSdkVersion="21" />
+</manifest>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/LibOne/res/values/values.xml
new file mode 100644
index 000000000000..d2dcea0c0b1e
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/res/values/values.xml
@@ -0,0 +1,28 @@
+<?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>
+ <public type="string" name="textview_text" />
+ <string name="textview_text">LibOne\'s textview_text string!</string>
+
+ <public type="attr" name="theme_attr" />
+ <attr name="theme_attr" format="string" />
+
+ <public type="style" name="Theme" />
+ <style name="Theme" parent="android:Theme.Material.Light.DarkActionBar">
+ <item name="theme_attr">[Please override with your own value]</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
new file mode 100644
index 000000000000..dc16d1bbb420
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
@@ -0,0 +1,29 @@
+#
+# 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_MODULE := AaptTestNamespace_LibTwo
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+# We need this to retain the R.java generated for this library.
+LOCAL_JAR_EXCLUDE_FILES := none
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/AndroidManifest.xml
new file mode 100644
index 000000000000..32944a9b8f0b
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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.libtwo">
+
+ <uses-sdk android:minSdkVersion="21" />
+</manifest>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/res/values/values.xml
new file mode 100644
index 000000000000..0c5f5d8d55e0
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/res/values/values.xml
@@ -0,0 +1,27 @@
+<?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>
+ <public type="string" name="public_string" />
+ <string name="public_string">LibTwo\'s public string!</string>
+
+ <public type="attr" name="textview_attr" />
+ <attr name="textview_attr" format="string" />
+
+ <declare-styleable name="TextView">
+ <attr name="textview_attr" />
+ </declare-styleable>
+</resources> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/src/com/android/aapt/namespace/libtwo/TextView.java b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/src/com/android/aapt/namespace/libtwo/TextView.java
new file mode 100644
index 000000000000..0f8024e58797
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/src/com/android/aapt/namespace/libtwo/TextView.java
@@ -0,0 +1,53 @@
+/*
+ * 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.libtwo;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+public class TextView extends android.widget.TextView {
+
+ private String mTextViewAttr;
+
+ public TextView(Context context) {
+ this(context, null);
+ }
+
+ public TextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public TextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ final TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TextView,
+ 0, 0);
+ try {
+ mTextViewAttr = ta.getString(R.styleable.TextView_textview_attr);
+ } finally {
+ ta.recycle();
+ }
+ }
+
+ public String getTextViewAttr() {
+ return mTextViewAttr;
+ }
+}
diff --git a/tools/aapt2/integration-tests/StaticLibTest/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/Android.mk
new file mode 100644
index 000000000000..6361f9b8ae7d
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTest/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH := $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/AppOne/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk
index 38bd5b5e3275..4d0c01d565a5 100644
--- a/tools/aapt2/integration-tests/AppOne/Android.mk
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk
@@ -18,12 +18,12 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
-LOCAL_PACKAGE_NAME := AaptTestAppOne
+LOCAL_PACKAGE_NAME := AaptTestStaticLib_App
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets $(LOCAL_PATH)/assets2
LOCAL_STATIC_ANDROID_LIBRARIES := \
- AaptTestStaticLibOne \
- AaptTestStaticLibTwo
+ AaptTestStaticLib_LibOne \
+ AaptTestStaticLib_LibTwo
LOCAL_AAPT_FLAGS := --no-version-vectors --no-version-transitions
include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml b/tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml
index a5f202dd22fc..a5f202dd22fc 100644
--- a/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml
diff --git a/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt b/tools/aapt2/integration-tests/StaticLibTest/App/assets/subdir/subsubdir/test.txt
index 125194943ec8..125194943ec8 100644
--- a/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/assets/subdir/subsubdir/test.txt
diff --git a/tools/aapt2/integration-tests/AppOne/assets/test.txt b/tools/aapt2/integration-tests/StaticLibTest/App/assets/test.txt
index 88266de2b4d4..88266de2b4d4 100644
--- a/tools/aapt2/integration-tests/AppOne/assets/test.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/assets/test.txt
diff --git a/tools/aapt2/integration-tests/AppOne/assets2/new.txt b/tools/aapt2/integration-tests/StaticLibTest/App/assets2/new.txt
index f4963a95503a..f4963a95503a 100644
--- a/tools/aapt2/integration-tests/AppOne/assets2/new.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/assets2/new.txt
diff --git a/tools/aapt2/integration-tests/AppOne/assets2/test.txt b/tools/aapt2/integration-tests/StaticLibTest/App/assets2/test.txt
index 5d8b36c0d52d..5d8b36c0d52d 100644
--- a/tools/aapt2/integration-tests/AppOne/assets2/test.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/assets2/test.txt
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/cheap_transparency.png
index 0522a9979db9..0522a9979db9 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/cheap_transparency.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/complex.9.png
index baf9fff13ab5..baf9fff13ab5 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/complex.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/icon.png
index 4bff9b900643..4bff9b900643 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/icon.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/image.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/image.xml
index 6132a75d85d0..6132a75d85d0 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/image.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/image.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/outline_8x8.9.png
index 7b331e16fcbd..7b331e16fcbd 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/outline_8x8.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_off_center_outline_32x16.9.png
index 0ec6c70a2b9f..0ec6c70a2b9f 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_off_center_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_outline_32x16.9.png
index e05708a089a3..e05708a089a3 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/round_rect_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/test.9.png
index 33daa117ea9d..33daa117ea9d 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/test.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_3x3.9.png
index a11377a0d670..a11377a0d670 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_optical_bounds_3x3.9.png
index 6803e4243484..6803e4243484 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/transparent_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_3x3.9.png
index 1a3731bbc8b8..1a3731bbc8b8 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_optical_bounds_3x3.9.png
index 489ace292e1f..489ace292e1f 100644
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/drawable/white_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/font/myfont-italic.ttf b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-italic.ttf
index e69de29bb2d1..e69de29bb2d1 100644
--- a/tools/aapt2/integration-tests/AppOne/res/font/myfont-italic.ttf
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-italic.ttf
diff --git a/tools/aapt2/integration-tests/AppOne/res/font/myfont-normal.ttf b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-normal.ttf
index e69de29bb2d1..e69de29bb2d1 100644
--- a/tools/aapt2/integration-tests/AppOne/res/font/myfont-normal.ttf
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont-normal.ttf
diff --git a/tools/aapt2/integration-tests/AppOne/res/font/myfont.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont.xml
index 1fb67914894e..1fb67914894e 100644
--- a/tools/aapt2/integration-tests/AppOne/res/font/myfont.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/font/myfont.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout-v21/main.xml
index 9f5a4a85cbcf..724bfe4a9536 100644
--- a/tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout-v21/main.xml
@@ -15,7 +15,6 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
diff --git a/tools/aapt2/integration-tests/AppOne/res/layout/main.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml
index ab1a251a7d56..aaa884bf7084 100644
--- a/tools/aapt2/integration-tests/AppOne/res/layout/main.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml
@@ -15,7 +15,6 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
diff --git a/tools/aapt2/integration-tests/AppOne/res/layout/special.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/special.xml
index 28c85ca92019..28c85ca92019 100644
--- a/tools/aapt2/integration-tests/AppOne/res/layout/special.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/special.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/navigation/home.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/navigation/home.xml
index ade271d60ab6..ade271d60ab6 100644
--- a/tools/aapt2/integration-tests/AppOne/res/navigation/home.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/navigation/home.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/raw/test.txt b/tools/aapt2/integration-tests/StaticLibTest/App/res/raw/test.txt
index b14df6442ea5..b14df6442ea5 100644
--- a/tools/aapt2/integration-tests/AppOne/res/raw/test.txt
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/raw/test.txt
diff --git a/tools/aapt2/integration-tests/AppOne/res/transition/transition_set.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/transition/transition_set.xml
index e10e6c2f53da..e10e6c2f53da 100644
--- a/tools/aapt2/integration-tests/AppOne/res/transition/transition_set.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/transition/transition_set.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/values-v4/styles.xml
index d8c11e210eda..d8c11e210eda 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/values-v4/styles.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/colors.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/colors.xml
index 4df5077051d2..4df5077051d2 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values/colors.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/colors.xml
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/styles.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/styles.xml
index 19d96c0809db..a088e5d0e1a2 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values/styles.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/styles.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
+<resources>
<style name="App">
<item name="android:background">@color/primary</item>
<item name="android:colorPrimary">@color/primary</item>
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/test.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/test.xml
index 2c9e8b877565..2c9e8b877565 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values/test.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/values/test.xml
diff --git a/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java b/tools/aapt2/integration-tests/StaticLibTest/App/src/com/android/aapt/app/one/AppOne.java
index 472b35a781fe..472b35a781fe 100644
--- a/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/src/com/android/aapt/app/one/AppOne.java
diff --git a/tools/aapt2/integration-tests/StaticLibOne/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk
index 0b7129aa0d38..0c828b80b3b3 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/Android.mk
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk
@@ -18,11 +18,11 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestStaticLibOne
+LOCAL_MODULE := AaptTestStaticLib_LibOne
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-# We need this to compile the Java sources of AaptTestStaticLibTwo using javac.
+# 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/StaticLibOne/AndroidManifest.xml b/tools/aapt2/integration-tests/StaticLibTest/LibOne/AndroidManifest.xml
index 705047e71300..705047e71300 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/AndroidManifest.xml
diff --git a/tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml b/tools/aapt2/integration-tests/StaticLibTest/LibOne/res/layout/layout.xml
index 683c91cd9cf5..683c91cd9cf5 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/res/layout/layout.xml
diff --git a/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml b/tools/aapt2/integration-tests/StaticLibTest/LibOne/res/values/values.xml
index b4dc90b3e640..b4dc90b3e640 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/res/values/values.xml
diff --git a/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java b/tools/aapt2/integration-tests/StaticLibTest/LibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
index cf48f67056cf..cf48f67056cf 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk
index 8b6eb41b08cd..538d5251b203 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/Android.mk
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk
@@ -18,10 +18,10 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestStaticLibTwo
+LOCAL_MODULE := AaptTestStaticLib_LibTwo
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLibOne
+LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLib_LibOne
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/AndroidManifest.xml
index 28f069932452..28f069932452 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/AndroidManifest.xml
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/res/drawable/vector.xml b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/drawable/vector.xml
index dd5979f7e838..dd5979f7e838 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/res/drawable/vector.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/drawable/vector.xml
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/layout/layout_two.xml
index ba9830708eb0..ba9830708eb0 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/layout/layout_two.xml
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/values/values.xml
index 97bb2a53d9f6..97bb2a53d9f6 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/res/values/values.xml
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
index 7110dcdd017a..7110dcdd017a 100644
--- a/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index ec1ddb8a07aa..02ee876014f7 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -21,7 +21,7 @@
#include "google/protobuf/message_lite.h"
-#include "flatten/Archive.h"
+#include "format/Archive.h"
#include "io/File.h"
#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 1f83fa098d74..c93461a66899 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -17,6 +17,7 @@
#include "java/AnnotationProcessor.h"
#include <algorithm>
+#include <array>
#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
@@ -41,30 +42,54 @@ StringPiece AnnotationProcessor::ExtractFirstSentence(const StringPiece& comment
return comment;
}
-void AnnotationProcessor::AppendCommentLine(std::string& comment) {
+struct AnnotationRule {
+ enum : uint32_t {
+ kDeprecated = 0x01,
+ kSystemApi = 0x02,
+ kTestApi = 0x04,
+ };
+
+ StringPiece doc_str;
+ uint32_t bit_mask;
+ StringPiece annotation;
+};
+
+static std::array<AnnotationRule, 2> sAnnotationRules = {{
+ {"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi"},
+ {"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi"},
+}};
+
+void AnnotationProcessor::AppendCommentLine(std::string comment) {
static const std::string sDeprecated = "@deprecated";
- static const std::string sSystemApi = "@SystemApi";
+ // Treat deprecated specially, since we don't remove it from the source comment.
if (comment.find(sDeprecated) != std::string::npos) {
- annotation_bit_mask_ |= kDeprecated;
+ annotation_bit_mask_ |= AnnotationRule::kDeprecated;
}
- std::string::size_type idx = comment.find(sSystemApi);
- if (idx != std::string::npos) {
- annotation_bit_mask_ |= kSystemApi;
- comment.erase(comment.begin() + idx,
- comment.begin() + idx + sSystemApi.size());
+ for (const AnnotationRule& rule : sAnnotationRules) {
+ std::string::size_type idx = comment.find(rule.doc_str.data());
+ if (idx != std::string::npos) {
+ annotation_bit_mask_ |= rule.bit_mask;
+ comment.erase(comment.begin() + idx, comment.begin() + idx + rule.doc_str.size());
+ }
}
- if (util::TrimWhitespace(comment).empty()) {
+ // Check if after removal of annotations the line is empty.
+ const StringPiece trimmed = util::TrimWhitespace(comment);
+ if (trimmed.empty()) {
return;
}
+ // If there was trimming to do, copy the string.
+ if (trimmed.size() != comment.size()) {
+ comment = trimmed.to_string();
+ }
+
if (!has_comments_) {
has_comments_ = true;
comment_ << "/**";
}
-
comment_ << "\n * " << std::move(comment);
}
@@ -73,16 +98,18 @@ void AnnotationProcessor::AppendComment(const StringPiece& comment) {
for (StringPiece line : util::Tokenize(comment, '\n')) {
line = util::TrimWhitespace(line);
if (!line.empty()) {
- std::string lineCopy = line.to_string();
- AppendCommentLine(lineCopy);
+ AppendCommentLine(line.to_string());
}
}
}
-void AnnotationProcessor::AppendNewLine() { comment_ << "\n *"; }
+void AnnotationProcessor::AppendNewLine() {
+ if (has_comments_) {
+ comment_ << "\n *";
+ }
+}
-void AnnotationProcessor::WriteToStream(std::ostream* out,
- const StringPiece& prefix) const {
+void AnnotationProcessor::WriteToStream(const StringPiece& prefix, std::ostream* out) const {
if (has_comments_) {
std::string result = comment_.str();
for (StringPiece line : util::Tokenize(result, '\n')) {
@@ -92,12 +119,14 @@ void AnnotationProcessor::WriteToStream(std::ostream* out,
<< "\n";
}
- if (annotation_bit_mask_ & kDeprecated) {
+ if (annotation_bit_mask_ & AnnotationRule::kDeprecated) {
*out << prefix << "@Deprecated\n";
}
- if (annotation_bit_mask_ & kSystemApi) {
- *out << prefix << "@android.annotation.SystemApi\n";
+ for (const AnnotationRule& rule : sAnnotationRules) {
+ if (annotation_bit_mask_ & rule.bit_mask) {
+ *out << prefix << rule.annotation << "\n";
+ }
}
}
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index a06eda0f9c5c..a7bf73f50de5 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -24,64 +24,53 @@
namespace aapt {
-/**
- * Builds a JavaDoc comment from a set of XML comments.
- * This will also look for instances of @SystemApi and convert them to
- * actual Java annotations.
- *
- * Example:
- *
- * Input XML:
- *
- * <!-- This is meant to be hidden because
- * It is system api. Also it is @deprecated
- * @SystemApi
- * -->
- *
- * Output JavaDoc:
- *
- * /\*
- * * This is meant to be hidden because
- * * It is system api. Also it is @deprecated
- * *\/
- *
- * Output Annotations:
- *
- * @Deprecated
- * @android.annotation.SystemApi
- *
- */
+// Builds a JavaDoc comment from a set of XML comments.
+// This will also look for instances of @SystemApi and convert them to
+// actual Java annotations.
+//
+// Example:
+//
+// Input XML:
+//
+// <!-- This is meant to be hidden because
+// It is system api. Also it is @deprecated
+// @SystemApi
+// -->
+//
+// Output JavaDoc:
+//
+// /**
+// * This is meant to be hidden because
+// * It is system api. Also it is @deprecated
+// */
+//
+// Output Annotations:
+//
+// @Deprecated
+// @android.annotation.SystemApi
class AnnotationProcessor {
public:
+ // Extracts the first sentence of a comment. The algorithm selects the substring starting from
+ // the beginning of the string, and ending at the first '.' character that is followed by a
+ // whitespace character. If these requirements are not met, the whole string is returned.
static android::StringPiece ExtractFirstSentence(const android::StringPiece& comment);
- /**
- * Adds more comments. Since resources can have various values with different
- * configurations,
- * we need to collect all the comments.
- */
+ // Adds more comments. Resources can have value definitions for various configurations, and
+ // each of the definitions may have comments that need to be processed.
void AppendComment(const android::StringPiece& comment);
void AppendNewLine();
- /**
- * Writes the comments and annotations to the stream, with the given prefix
- * before each line.
- */
- void WriteToStream(std::ostream* out, const android::StringPiece& prefix) const;
+ // Writes the comments and annotations to the stream, with the given prefix before each line.
+ void WriteToStream(const android::StringPiece& prefix, std::ostream* out) const;
private:
- enum : uint32_t {
- kDeprecated = 0x01,
- kSystemApi = 0x02,
- };
-
std::stringstream comment_;
std::stringstream mAnnotations;
bool has_comments_ = false;
uint32_t annotation_bit_mask_ = 0;
- void AppendCommentLine(std::string& line);
+ void AppendCommentLine(std::string line);
};
} // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index 9ccac8888426..856f4ccbd7f0 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -34,7 +34,7 @@ TEST(AnnotationProcessorTest, EmitsDeprecated) {
processor.AppendComment(comment);
std::stringstream result;
- processor.WriteToStream(&result, "");
+ processor.WriteToStream("", &result);
std::string annotations = result.str();
EXPECT_THAT(annotations, HasSubstr("@Deprecated"));
@@ -45,7 +45,7 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
processor.AppendComment("@SystemApi This is a system API");
std::stringstream result;
- processor.WriteToStream(&result, "");
+ processor.WriteToStream("", &result);
std::string annotations = result.str();
EXPECT_THAT(annotations, HasSubstr("@android.annotation.SystemApi"));
@@ -53,6 +53,19 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
EXPECT_THAT(annotations, HasSubstr("This is a system API"));
}
+TEST(AnnotationProcessorTest, EmitsTestApiAnnotationAndRemovesFromComment) {
+ AnnotationProcessor processor;
+ processor.AppendComment("@TestApi This is a test API");
+
+ std::stringstream result;
+ processor.WriteToStream("", &result);
+ std::string annotations = result.str();
+
+ EXPECT_THAT(annotations, HasSubstr("@android.annotation.TestApi"));
+ EXPECT_THAT(annotations, Not(HasSubstr("@TestApi")));
+ EXPECT_THAT(annotations, HasSubstr("This is a test API"));
+}
+
TEST(AnnotationProcessor, ExtractsFirstSentence) {
EXPECT_THAT(AnnotationProcessor::ExtractFirstSentence("This is the only sentence"),
Eq("This is the only sentence"));
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index c139b73db296..0c57e7e06128 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -18,12 +18,12 @@
#include "androidfw/StringPiece.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
void ClassMember::WriteToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
- processor_.WriteToStream(out, prefix);
+ processor_.WriteToStream(prefix, out);
}
void MethodDefinition::AppendStatement(const StringPiece& statement) {
@@ -100,9 +100,8 @@ constexpr static const char* sWarningHeader =
" * should not be modified by hand.\n"
" */\n\n";
-bool ClassDefinition::WriteJavaFile(const ClassDefinition* def,
- const StringPiece& package, bool final,
- std::ostream* out) {
+bool ClassDefinition::WriteJavaFile(const ClassDefinition* def, const StringPiece& package,
+ bool final, std::ostream* out) {
*out << sWarningHeader << "package " << package << ";\n\n";
def->WriteToStream("", final, out);
return bool(*out);
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 44fa0f19a0e5..8da9106aa8d7 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -480,7 +480,7 @@ Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& packa
if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
// The entry name was mangled, and we successfully unmangled it.
// Check that we want to emit this symbol.
- if (package_name != unmangled_package) {
+ if (package_name_to_generate != unmangled_package) {
// Skip the entry if it doesn't belong to the package we're writing.
return {};
}
@@ -579,8 +579,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
continue;
}
- // Stay consistent with AAPT and generate an empty type class if the R class
- // is public.
+ // Stay consistent with AAPT and generate an empty type class if the R class is public.
const bool force_creation_if_empty =
(options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 84bf04134ad9..668e4340e839 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -22,9 +22,11 @@
#include "test/Test.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::android::StringPiece;
+using ::testing::HasSubstr;
using ::testing::Lt;
using ::testing::Ne;
+using ::testing::Not;
namespace aapt {
@@ -54,17 +56,15 @@ TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
.AddSimple("android:id/hey-man", ResourceId(0x01020000))
.AddValue("android:attr/cool.attr", ResourceId(0x01010000),
test::AttributeBuilder(false).Build())
- .AddValue(
- "android:styleable/hey.dude", ResourceId(0x01030000),
- test::StyleableBuilder()
- .AddItem("android:attr/cool.attr", ResourceId(0x01010000))
- .Build())
+ .AddValue("android:styleable/hey.dude", ResourceId(0x01030000),
+ test::StyleableBuilder()
+ .AddItem("android:attr/cool.attr", ResourceId(0x01010000))
+ .Build())
.Build();
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
@@ -74,14 +74,9 @@ TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
std::string output = out.str();
- EXPECT_NE(std::string::npos,
- output.find("public static final int hey_man=0x01020000;"));
-
- EXPECT_NE(std::string::npos,
- output.find("public static final int[] hey_dude={"));
-
- EXPECT_NE(std::string::npos,
- output.find("public static final int hey_dude_cool_attr=0;"));
+ EXPECT_THAT(output, HasSubstr("public static final int hey_man=0x01020000;"));
+ EXPECT_THAT(output, HasSubstr("public static final int[] hey_dude={"));
+ EXPECT_THAT(output, HasSubstr("public static final int hey_dude_cool_attr=0;"));
}
TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
@@ -94,8 +89,7 @@ TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
@@ -103,11 +97,10 @@ TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int one=0x01020000;"));
- EXPECT_EQ(std::string::npos, output.find("two"));
- EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
+ EXPECT_THAT(output, HasSubstr("package com.android.internal;"));
+ EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
+ EXPECT_THAT(output, Not(HasSubstr("two")));
+ EXPECT_THAT(output, Not(HasSubstr("com_foo$two")));
}
TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
@@ -120,8 +113,7 @@ TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
@@ -129,9 +121,8 @@ TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
ASSERT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("public static final class attr"));
- EXPECT_EQ(std::string::npos,
- output.find("public static final class ^attr-private"));
+ EXPECT_THAT(output, HasSubstr("public static final class attr"));
+ EXPECT_THAT(output, Not(HasSubstr("public static final class ^attr-private")));
}
TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
@@ -142,16 +133,13 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
.AddSimple("android:id/one", ResourceId(0x01020000))
.AddSimple("android:id/two", ResourceId(0x01020001))
.AddSimple("android:id/three", ResourceId(0x01020002))
- .SetSymbolState("android:id/one", ResourceId(0x01020000),
- SymbolState::kPublic)
- .SetSymbolState("android:id/two", ResourceId(0x01020001),
- SymbolState::kPrivate)
+ .SetSymbolState("android:id/one", ResourceId(0x01020000), SymbolState::kPublic)
+ .SetSymbolState("android:id/two", ResourceId(0x01020001), SymbolState::kPrivate)
.Build();
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
@@ -162,10 +150,9 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
- EXPECT_NE(std::string::npos,
- output.find("public static final int one=0x01020000;"));
- EXPECT_EQ(std::string::npos, output.find("two"));
- EXPECT_EQ(std::string::npos, output.find("three"));
+ EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
+ EXPECT_THAT(output, Not(HasSubstr("two")));
+ EXPECT_THAT(output, Not(HasSubstr("three")));
}
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
@@ -174,11 +161,9 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
- EXPECT_NE(std::string::npos,
- output.find("public static final int one=0x01020000;"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int two=0x01020001;"));
- EXPECT_EQ(std::string::npos, output.find("three"));
+ EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
+ EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
+ EXPECT_THAT(output, Not(HasSubstr("three")));
}
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
@@ -187,12 +172,9 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
- EXPECT_NE(std::string::npos,
- output.find("public static final int one=0x01020000;"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int two=0x01020001;"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int three=0x01020002;"));
+ EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
+ EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
+ EXPECT_THAT(output, HasSubstr("public static final int three=0x01020002;"));
}
}
@@ -248,8 +230,7 @@ TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
@@ -258,8 +239,8 @@ TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
EXPECT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("int foo_bar="));
- EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar="));
+ EXPECT_THAT(output, HasSubstr("int foo_bar="));
+ EXPECT_THAT(output, HasSubstr("int foo_com_lib_bar="));
}
TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
@@ -273,24 +254,22 @@ TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
- std::string actual = out.str();
+ std::string output = out.str();
- const char* expectedText =
+ const char* expected_text =
R"EOF(/**
* This is a comment
* @deprecated
*/
@Deprecated
public static final int foo=0x01010000;)EOF";
-
- EXPECT_NE(std::string::npos, actual.find(expectedText));
+ EXPECT_THAT(output, HasSubstr(expected_text));
}
TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
@@ -300,8 +279,7 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent)
attr.SetComment(StringPiece("This is an attribute"));
Styleable styleable;
- styleable.entries.push_back(
- Reference(test::ParseNameOrDie("android:attr/one")));
+ styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
styleable.SetComment(StringPiece("This is a styleable"));
std::unique_ptr<ResourceTable> table =
@@ -314,8 +292,7 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent)
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGeneratorOptions options;
@@ -323,12 +300,12 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent)
JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
- std::string actual = out.str();
+ std::string output = out.str();
- EXPECT_NE(std::string::npos, actual.find("attr name android:one"));
- EXPECT_NE(std::string::npos, actual.find("attr description"));
- EXPECT_NE(std::string::npos, actual.find(attr.GetComment().data()));
- EXPECT_NE(std::string::npos, actual.find(styleable.GetComment().data()));
+ EXPECT_THAT(output, HasSubstr("attr name android:one"));
+ EXPECT_THAT(output, HasSubstr("attr description"));
+ EXPECT_THAT(output, HasSubstr(attr.GetComment()));
+ EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
}
TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
@@ -390,8 +367,7 @@ TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGeneratorOptions options;
@@ -399,17 +375,17 @@ TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
- std::string actual = out.str();
+ std::string output = out.str();
- EXPECT_EQ(std::string::npos, actual.find("@attr name android:one"));
- EXPECT_EQ(std::string::npos, actual.find("@attr description"));
+ EXPECT_THAT(output, Not(HasSubstr("@attr name android:one")));
+ EXPECT_THAT(output, Not(HasSubstr("@attr description")));
// We should find @removed only in the attribute javadoc and not anywhere else
- // (i.e. the class
- // javadoc).
- const size_t pos = actual.find("removed");
- EXPECT_NE(std::string::npos, pos);
- EXPECT_EQ(std::string::npos, actual.find("removed", pos + 1));
+ // (i.e. the class javadoc).
+ const std::string kRemoved("removed");
+ ASSERT_THAT(output, HasSubstr(kRemoved));
+ std::string after_first_match = output.substr(output.find(kRemoved) + kRemoved.size());
+ EXPECT_THAT(after_first_match, Not(HasSubstr(kRemoved)));
}
TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) {
@@ -430,19 +406,17 @@ TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary)
JavaClassGeneratorOptions options;
options.use_final = false;
- options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{
- {"com.foo", "com.boo"},
- };
+ options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{{"com.foo", "com.boo"}};
JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
- std::string actual = out.str();
+ std::string output = out.str();
- EXPECT_NE(std::string::npos, actual.find("void onResourcesLoaded"));
- EXPECT_NE(std::string::npos, actual.find("com.foo.R.onResourcesLoaded"));
- EXPECT_NE(std::string::npos, actual.find("com.boo.R.onResourcesLoaded"));
+ EXPECT_THAT(output, HasSubstr("void onResourcesLoaded"));
+ EXPECT_THAT(output, HasSubstr("com.foo.R.onResourcesLoaded"));
+ EXPECT_THAT(output, HasSubstr("com.boo.R.onResourcesLoaded"));
}
} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index cad4c6c7c94f..c4b36176aa71 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -21,24 +21,21 @@
#include "Source.h"
#include "java/AnnotationProcessor.h"
#include "java/ClassDefinition.h"
+#include "text/Unicode.h"
#include "util/Maybe.h"
#include "xml/XmlDom.h"
-using android::StringPiece;
+using ::android::StringPiece;
+using ::aapt::text::IsJavaIdentifier;
namespace aapt {
-static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag,
- const Source& source,
- const StringPiece& value) {
- const StringPiece sep = ".";
- auto iter = std::find_end(value.begin(), value.end(), sep.begin(), sep.end());
-
- StringPiece result;
- if (iter != value.end()) {
- result.assign(iter + sep.size(), value.end() - (iter + sep.size()));
- } else {
- result = value;
+static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
+ const std::string& value) {
+ StringPiece result = value;
+ size_t pos = value.rfind('.');
+ if (pos != std::string::npos) {
+ result = result.substr(pos + 1);
}
if (result.empty()) {
@@ -46,33 +43,23 @@ static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag,
return {};
}
- iter = util::FindNonAlphaNumericAndNotInSet(result, "_");
- if (iter != result.end()) {
- diag->Error(DiagMessage(source) << "invalid character '"
- << StringPiece(iter, 1) << "' in '"
- << result << "'");
+ if (!IsJavaIdentifier(result)) {
+ diag->Error(DiagMessage(source) << "invalid Java identifier '" << result << "'");
return {};
}
-
- if (*result.begin() >= '0' && *result.begin() <= '9') {
- diag->Error(DiagMessage(source) << "symbol can not start with a digit");
- return {};
- }
-
return result;
}
-static bool WriteSymbol(const Source& source, IDiagnostics* diag,
- xml::Element* el, ClassDefinition* class_def) {
+static bool WriteSymbol(const Source& source, IDiagnostics* diag, xml::Element* el,
+ ClassDefinition* class_def) {
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name");
if (!attr) {
- diag->Error(DiagMessage(source) << "<" << el->name
- << "> must define 'android:name'");
+ diag->Error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
return false;
}
- Maybe<StringPiece> result = ExtractJavaIdentifier(
- diag, source.WithLine(el->line_number), attr->value);
+ Maybe<StringPiece> result =
+ ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value);
if (!result) {
return false;
}
@@ -88,8 +75,7 @@ static bool WriteSymbol(const Source& source, IDiagnostics* diag,
return true;
}
-std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
- xml::XmlResource* res) {
+std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag, xml::XmlResource* res) {
xml::Element* el = xml::FindRootElement(res->root.get());
if (!el) {
diag->Error(DiagMessage(res->file.source) << "no root tag defined");
@@ -97,8 +83,7 @@ std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
}
if (el->name != "manifest" && !el->namespace_uri.empty()) {
- diag->Error(DiagMessage(res->file.source)
- << "no <manifest> root tag defined");
+ diag->Error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
return {};
}
@@ -112,11 +97,9 @@ std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
for (xml::Element* child_el : children) {
if (child_el->namespace_uri.empty()) {
if (child_el->name == "permission") {
- error |= !WriteSymbol(res->file.source, diag, child_el,
- permission_class.get());
+ error |= !WriteSymbol(res->file.source, diag, child_el, permission_class.get());
} else if (child_el->name == "permission-group") {
- error |= !WriteSymbol(res->file.source, diag, child_el,
- permission_group_class.get());
+ error |= !WriteSymbol(res->file.source, diag, child_el, permission_group_class.get());
}
}
}
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index 44b6a1ffd5ae..ada563409d19 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -84,6 +84,8 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
@hide
@SystemApi -->
<permission android:name="android.permission.SECRET" />
+ <!-- @TestApi This is a test only permission. -->
+ <permission android:name="android.permission.TEST_ONLY" />
</manifest>)");
std::string actual;
@@ -110,6 +112,13 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
@android.annotation.SystemApi
public static final String SECRET="android.permission.SECRET";)";
EXPECT_THAT(actual, HasSubstr(expected_secret));
+
+ const char* expected_test = R"( /**
+ * This is a test only permission.
+ */
+ @android.annotation.TestApi
+ public static final String TEST_ONLY="android.permission.TEST_ONLY";)";
+ EXPECT_THAT(actual, HasSubstr(expected_test));
}
// This is bad but part of public API behaviour so we need to preserve it.
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 5527f9092c87..3c9c4767b3d1 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -21,6 +21,7 @@
#include <unordered_set>
#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
#include "Resource.h"
#include "SdkConstants.h"
@@ -33,18 +34,15 @@ class ResourceTable;
class ResourceEntry;
struct ConfigDescription;
-/**
- * Defines the location in which a value exists. This determines visibility of
- * other package's private symbols.
- */
+// Defines the context in which a resource value is defined. Most resources are defined with the
+// implicit package name of their compilation context. Understanding the package name of a resource
+// allows to determine visibility of other symbols which may or may not have their packages defined.
struct CallSite {
- ResourceNameRef resource;
+ std::string package;
};
-/**
- * Determines whether a versioned resource should be created. If a versioned
- * resource already exists, it takes precedence.
- */
+// Determines whether a versioned resource should be created. If a versioned resource already
+// exists, it takes precedence.
bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
const ApiVersion sdk_version_to_generate);
@@ -62,39 +60,26 @@ class AutoVersioner : public IResourceTableConsumer {
DISALLOW_COPY_AND_ASSIGN(AutoVersioner);
};
-/**
- * If any attribute resource values are defined as public, this consumer will
- * move all private
- * attribute resource values to a private ^private-attr type, avoiding backwards
- * compatibility
- * issues with new apps running on old platforms.
- *
- * The Android platform ignores resource attributes it doesn't recognize, so an
- * app developer can
- * use new attributes in their layout XML files without worrying about
- * versioning. This assumption
- * actually breaks on older platforms. OEMs may add private attributes that are
- * used internally.
- * AAPT originally assigned all private attributes IDs immediately proceeding
- * the public attributes'
- * IDs.
- *
- * This means that on a newer Android platform, an ID previously assigned to a
- * private attribute
- * may end up assigned to a public attribute.
- *
- * App developers assume using the newer attribute is safe on older platforms
- * because it will
- * be ignored. Instead, the platform thinks the new attribute is an older,
- * private attribute and
- * will interpret it as such. This leads to unintended styling and exceptions
- * thrown due to
- * unexpected types.
- *
- * By moving the private attributes to a completely different type, this ID
- * conflict will never
- * occur.
- */
+// If any attribute resource values are defined as public, this consumer will move all private
+// attribute resource values to a private ^private-attr type, avoiding backwards compatibility
+// issues with new apps running on old platforms.
+//
+// The Android platform ignores resource attributes it doesn't recognize, so an app developer can
+// use new attributes in their layout XML files without worrying about versioning. This assumption
+// actually breaks on older platforms. OEMs may add private attributes that are used internally.
+// AAPT originally assigned all private attributes IDs immediately proceeding the public attributes'
+// IDs.
+//
+// This means that on a newer Android platform, an ID previously assigned to a private attribute
+// may end up assigned to a public attribute.
+//
+// App developers assume using the newer attribute is safe on older platforms because it will
+// be ignored. Instead, the platform thinks the new attribute is an older, private attribute and
+// will interpret it as such. This leads to unintended styling and exceptions thrown due to
+// unexpected types.
+//
+// By moving the private attributes to a completely different type, this ID conflict will never
+// occur.
class PrivateAttributeMover : public IResourceTableConsumer {
public:
PrivateAttributeMover() = default;
@@ -126,14 +111,10 @@ class ProductFilter : public IResourceTableConsumer {
std::unordered_set<std::string> products_;
};
-/**
- * Removes namespace nodes and URI information from the XmlResource.
- *
- * Once an XmlResource is processed by this consumer, it is no longer able to
- * have its attributes
- * parsed. As such, this XmlResource must have already been processed by
- * XmlReferenceLinker.
- */
+// Removes namespace nodes and URI information from the XmlResource.
+//
+// Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
+// parsed. As such, this XmlResource must have already been processed by XmlReferenceLinker.
class XmlNamespaceRemover : public IXmlResourceConsumer {
public:
explicit XmlNamespaceRemover(bool keep_uris = false) : keep_uris_(keep_uris){};
@@ -146,11 +127,8 @@ class XmlNamespaceRemover : public IXmlResourceConsumer {
bool keep_uris_;
};
-/**
- * Resolves attributes in the XmlResource and compiles string values to resource
- * values.
- * Once an XmlResource is processed by this linker, it is ready to be flattened.
- */
+// Resolves attributes in the XmlResource and compiles string values to resource values.
+// Once an XmlResource is processed by this linker, it is ready to be flattened.
class XmlReferenceLinker : public IXmlResourceConsumer {
public:
XmlReferenceLinker() = default;
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 414e56eb5dcc..ad7d8b65350d 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -30,26 +30,21 @@
#include "util/Util.h"
#include "xml/XmlUtil.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
namespace {
-/**
- * The ReferenceLinkerVisitor will follow all references and make sure they
- * point
- * to resources that actually exist, either in the local resource table, or as
- * external
- * symbols. Once the target resource has been found, the ID of the resource will
- * be assigned
- * to the reference object.
- *
- * NOTE: All of the entries in the ResourceTable must be assigned IDs.
- */
-class ReferenceLinkerVisitor : public ValueVisitor {
+// The ReferenceLinkerVisitor will follow all references and make sure they point
+// to resources that actually exist, either in the local resource table, or as external
+// symbols. Once the target resource has been found, the ID of the resource will be assigned
+// to the reference object.
+//
+// NOTE: All of the entries in the ResourceTable must be assigned IDs.
+class ReferenceLinkerVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
StringPool* string_pool, xml::IPackageDeclStack* decl)
@@ -65,14 +60,9 @@ class ReferenceLinkerVisitor : public ValueVisitor {
}
}
- /**
- * We visit the Style specially because during this phase, values of
- * attributes are
- * all RawString values. Now that we are expected to resolve all symbols, we
- * can
- * lookup the attributes to find out which types are allowed for the
- * attributes' values.
- */
+ // We visit the Style specially because during this phase, values of attributes are
+ // all RawString values. Now that we are expected to resolve all symbols, we can
+ // lookup the attributes to find out which types are allowed for the attributes' values.
void Visit(Style* style) override {
if (style->parent) {
Visit(&style->parent.value());
@@ -81,28 +71,21 @@ class ReferenceLinkerVisitor : public ValueVisitor {
for (Style::Entry& entry : style->entries) {
std::string err_str;
- // Transform the attribute reference so that it is using the fully
- // qualified package
- // name. This will also mark the reference as being able to see private
- // resources if
- // there was a '*' in the reference or if the package came from the
- // private namespace.
+ // Transform the attribute reference so that it is using the fully qualified package
+ // name. This will also mark the reference as being able to see private resources if
+ // there was a '*' in the reference or if the package came from the private namespace.
Reference transformed_reference = entry.key;
- TransformReferenceFromNamespace(package_decls_,
- context_->GetCompilationPackage(),
- &transformed_reference);
+ ResolvePackage(package_decls_, &transformed_reference);
- // Find the attribute in the symbol table and check if it is visible from
- // this callsite.
+ // 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);
if (symbol) {
- // Assign our style key the correct ID.
- // The ID may not exist.
+ // Assign our style key the correct ID. The ID may not exist.
entry.key.id = symbol->id;
- // Try to convert the value to a more specific, typed value based on the
- // attribute it is set to.
+ // Try to convert the value to a more specific, typed value based on the attribute it is
+ // set to.
entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
// Link/resolve the final value (mostly if it's a reference).
@@ -115,8 +98,8 @@ class ReferenceLinkerVisitor : public ValueVisitor {
// The actual type of this item is incompatible with the attribute.
DiagMessage msg(entry.key.GetSource());
- // Call the matches method again, this time with a DiagMessage so we
- // fill in the actual error message.
+ // Call the matches method again, this time with a DiagMessage so we fill in the actual
+ // error message.
symbol->attribute->Matches(*entry.value, &msg);
context_->GetDiagnostics()->Error(msg);
error_ = true;
@@ -125,7 +108,7 @@ class ReferenceLinkerVisitor : public ValueVisitor {
} else {
DiagMessage msg(entry.key.GetSource());
msg << "style attribute '";
- ReferenceLinker::WriteResourceName(&msg, entry.key, transformed_reference);
+ ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
msg << "' " << err_str;
context_->GetDiagnostics()->Error(msg);
error_ = true;
@@ -133,17 +116,15 @@ class ReferenceLinkerVisitor : public ValueVisitor {
}
}
- bool HasError() { return error_; }
+ bool HasError() {
+ return error_;
+ }
private:
DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
- /**
- * Transform a RawString value into a more specific, appropriate value, based
- * on the
- * Attribute. If a non RawString value is passed in, this is an identity
- * transform.
- */
+ // Transform a RawString value into a more specific, appropriate value, based on the
+ // Attribute. If a non RawString value is passed in, this is an identity transform.
std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
const Attribute* attr) {
if (RawString* raw_string = ValueCast<RawString>(value.get())) {
@@ -178,11 +159,9 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
public:
EmptyDeclStack() = default;
- Maybe<xml::ExtractedPackage> TransformPackageAlias(
- const StringPiece& alias,
- const StringPiece& local_package) const override {
+ Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
if (alias.empty()) {
- return xml::ExtractedPackage{local_package.to_string(), true /* private */};
+ return xml::ExtractedPackage{{}, true /*private*/};
}
return {};
}
@@ -191,32 +170,44 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
};
-} // namespace
+// The symbol is visible if it is public, or if the reference to it is requesting private access
+// or if the callsite comes from the same package.
+bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
+ const CallSite& callsite) {
+ if (symbol.is_public || ref.private_reference) {
+ return true;
+ }
-/**
- * The symbol is visible if it is public, or if the reference to it is
- * requesting private access
- * or if the callsite comes from the same package.
- */
-bool ReferenceLinker::IsSymbolVisible(const SymbolTable::Symbol& symbol,
- const Reference& ref,
- const CallSite& callsite) {
- if (!symbol.is_public && !ref.private_reference) {
- if (ref.name) {
- return callsite.resource.package == ref.name.value().package;
- } else if (ref.id && symbol.id) {
- return ref.id.value().package_id() == symbol.id.value().package_id();
- } else {
- return false;
+ if (ref.name) {
+ const ResourceName& name = ref.name.value();
+ if (name.package.empty()) {
+ // If the symbol was found, and the package is empty, that means it was found in the local
+ // scope, which is always visible (private local).
+ return true;
}
+
+ // The symbol is visible if the reference is local to the same package it is defined in.
+ return callsite.package == name.package;
}
- return true;
+
+ if (ref.id && symbol.id) {
+ return ref.id.value().package_id() == symbol.id.value().package_id();
+ }
+ return false;
}
+} // namespace
+
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
+ const CallSite& callsite,
SymbolTable* symbols) {
if (reference.name) {
- return symbols->FindByName(reference.name.value());
+ 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));
+ }
+ return symbols->FindByName(name);
} else if (reference.id) {
return symbols->FindById(reference.id.value());
} else {
@@ -228,7 +219,7 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const R
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error) {
- const SymbolTable::Symbol* symbol = ResolveSymbol(reference, symbols);
+ const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
if (!symbol) {
if (out_error) *out_error = "not found";
return nullptr;
@@ -274,24 +265,62 @@ Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference&
return xml::AaptAttribute(*symbol->attribute, symbol->id);
}
-void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
- const Reference& orig,
- const Reference& transformed) {
+void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
CHECK(out_msg != nullptr);
+ if (!ref.name) {
+ *out_msg << ref.id.value();
+ return;
+ }
- if (orig.name) {
- *out_msg << orig.name.value();
- if (transformed.name.value() != orig.name.value()) {
- *out_msg << " (aka " << transformed.name.value() << ")";
- }
- } else {
- *out_msg << orig.id.value();
+ *out_msg << ref.name.value();
+
+ Reference fully_qualified = ref;
+ xml::ResolvePackage(decls, &fully_qualified);
+
+ ResourceName& full_name = fully_qualified.name.value();
+ if (full_name.package.empty()) {
+ full_name.package = callsite.package;
+ }
+
+ if (full_name != ref.name.value()) {
+ *out_msg << " (aka " << full_name << ")";
+ }
+}
+
+void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls,
+ DiagMessage* out_msg) {
+ CHECK(out_msg != nullptr);
+ if (!ref.name) {
+ *out_msg << ref.id.value();
+ return;
+ }
+
+ const ResourceName& ref_name = ref.name.value();
+ CHECK_EQ(ref_name.type, ResourceType::kAttr);
+
+ if (!ref_name.package.empty()) {
+ *out_msg << ref_name.package << ":";
+ }
+ *out_msg << ref_name.entry;
+
+ Reference fully_qualified = ref;
+ xml::ResolvePackage(decls, &fully_qualified);
+
+ ResourceName& full_name = fully_qualified.name.value();
+ if (full_name.package.empty()) {
+ full_name.package = callsite.package;
+ }
+
+ if (full_name != ref.name.value()) {
+ *out_msg << " (aka " << full_name.package << ":" << full_name.entry << ")";
}
}
bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
IAaptContext* context, SymbolTable* symbols,
- xml::IPackageDeclStack* decls) {
+ const xml::IPackageDeclStack* decls) {
CHECK(reference != nullptr);
if (!reference->name && !reference->id) {
// This is @null.
@@ -299,7 +328,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
}
Reference transformed_reference = *reference;
- TransformReferenceFromNamespace(decls, context->GetCompilationPackage(), &transformed_reference);
+ xml::ResolvePackage(decls, &transformed_reference);
std::string err_str;
const SymbolTable::Symbol* s =
@@ -314,7 +343,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
DiagMessage error_msg(reference->GetSource());
error_msg << "resource ";
- WriteResourceName(&error_msg, *reference, transformed_reference);
+ WriteResourceName(*reference, callsite, decls, &error_msg);
error_msg << " " << err_str;
context->GetDiagnostics()->Error(error_msg);
return false;
@@ -324,21 +353,24 @@ bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
EmptyDeclStack decl_stack;
bool error = false;
for (auto& package : table->packages) {
+ // Since we're linking, each package must have a name.
+ CHECK(!package->name.empty()) << "all packages being linked must have a name";
+
for (auto& type : package->types) {
for (auto& entry : type->entries) {
- // Symbol state information may be lost if there is no value for the
- // resource.
- if (entry->symbol_status.state != SymbolState::kUndefined &&
- entry->values.empty()) {
- context->GetDiagnostics()->Error(
- DiagMessage(entry->symbol_status.source)
- << "no definition for declared symbol '"
- << ResourceNameRef(package->name, type->type, entry->name)
- << "'");
+ // First, unmangle the name if necessary.
+ ResourceName name(package->name, type->type, entry->name);
+ NameMangler::Unmangle(&name.entry, &name.package);
+
+ // Symbol state information may be lost if there is no value for the resource.
+ if (entry->symbol_status.state != SymbolState::kUndefined && entry->values.empty()) {
+ context->GetDiagnostics()->Error(DiagMessage(entry->symbol_status.source)
+ << "no definition for declared symbol '" << name << "'");
error = true;
}
- CallSite callsite = {ResourceNameRef(package->name, type->type, entry->name)};
+ // The context of this resource is the package in which it is defined.
+ const CallSite callsite{name.package};
ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
&table->string_pool, &decl_stack);
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index b3d0196d9e7c..b0b49457e5dd 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -21,7 +21,6 @@
#include "Resource.h"
#include "ResourceValues.h"
-#include "ValueVisitor.h"
#include "link/Linkers.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
@@ -29,83 +28,58 @@
namespace aapt {
-/**
- * Resolves all references to resources in the ResourceTable and assigns them
- * IDs.
- * The ResourceTable must already have IDs assigned to each resource.
- * Once the ResourceTable is processed by this linker, it is ready to be
- * flattened.
- */
+// Resolves all references to resources in the ResourceTable and assigns them IDs.
+// The ResourceTable must already have IDs assigned to each resource.
+// Once the ResourceTable is processed by this linker, it is ready to be flattened.
class ReferenceLinker : public IResourceTableConsumer {
public:
ReferenceLinker() = default;
- /**
- * Returns true if the symbol is visible by the reference and from the
- * callsite.
- */
- static bool IsSymbolVisible(const SymbolTable::Symbol& symbol,
- const Reference& ref, const CallSite& callsite);
-
- /**
- * Performs name mangling and looks up the resource in the symbol table.
- * Returns nullptr if the symbol was not found.
- */
- static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference, SymbolTable* symbols);
-
- /**
- * 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. out_error holds the error message.
- */
+ // 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).
+ // Returns nullptr if the symbol was not found.
+ static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
+ const CallSite& callsite, SymbolTable* symbols);
+
+ // 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.
+ // `out_error` holds the error message.
static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error);
- /**
- * Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is
- * an attribute.
- * That is, the return value will have a non-null value for
- * ISymbolTable::Symbol::attribute.
- */
+ // Same as ResolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
+ // That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
std::string* out_error);
- /**
- * Resolves the attribute reference and returns an xml::AaptAttribute if
- * successful.
- * If resolution fails, outError holds the error message.
- */
+ // 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,
std::string* out_error);
- /**
- * Writes the resource name to the DiagMessage, using the
- * "orig_name (aka <transformed_name>)" syntax.
- */
- static void WriteResourceName(DiagMessage* out_msg, const Reference& orig,
- const Reference& transformed);
-
- /**
- * Transforms the package name of the reference to the fully qualified package
- * name using
- * the xml::IPackageDeclStack, then mangles and looks up the symbol. If the
- * symbol is visible
- * to the reference at the callsite, the reference is updated with an ID.
- * Returns false on failure, and an error message is logged to the
- * IDiagnostics in the context.
- */
+ // Writes the resource name to the DiagMessage, using the
+ // "orig_name (aka <transformed_name>)" syntax.
+ static void WriteResourceName(const Reference& orig, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
+
+ // Same as WriteResourceName but omits the 'attr' part.
+ static void WriteAttributeName(const Reference& ref, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
+
+ // Transforms the package name of the reference to the fully qualified package name using
+ // the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
+ // to the reference at the callsite, the reference is updated with an ID.
+ // Returns false on failure, and an error message is logged to the IDiagnostics in the context.
static bool LinkReference(const CallSite& callsite, Reference* reference, IAaptContext* context,
- SymbolTable* symbols, xml::IPackageDeclStack* decls);
+ SymbolTable* symbols, const xml::IPackageDeclStack* decls);
- /**
- * Links all references in the ResourceTable.
- */
+ // Links all references in the ResourceTable.
bool Consume(IAaptContext* context, ResourceTable* table) override;
private:
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 72a91689e392..be38b967c986 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -18,7 +18,9 @@
#include "test/Test.h"
-using android::ResTable_map;
+using ::android::ResTable_map;
+using ::testing::Eq;
+using ::testing::IsNull;
using ::testing::NotNull;
namespace aapt {
@@ -263,7 +265,7 @@ TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic)
.Build());
std::string error;
- const CallSite call_site{ResourceNameRef("com.app.test", ResourceType::kString, "foo")};
+ const CallSite call_site{"com.app.test"};
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
*test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
ASSERT_THAT(symbol, NotNull());
@@ -281,7 +283,7 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
.Build());
std::string error;
- const CallSite call_site{ResourceNameRef("com.app.ext", ResourceType::kLayout, "foo")};
+ const CallSite call_site{"com.app.ext"};
EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
*test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
@@ -293,4 +295,27 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
EXPECT_TRUE(error.empty());
}
+TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) {
+ NameMangler mangler(NameManglerPolicy{"com.app.test"});
+ SymbolTable table(&mangler);
+ table.AppendSource(test::StaticSymbolSourceBuilder()
+ .AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000))
+ .AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
+ .Build());
+
+ const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
+ CallSite{"com.app.test"}, &table);
+ 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);
+ 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),
+ IsNull());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 10e837c725e5..93c904f1a743 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -24,7 +24,7 @@
#include "ValueVisitor.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
@@ -32,27 +32,23 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table,
const TableMergerOptions& options)
: context_(context), master_table_(out_table), options_(options) {
// Create the desired package that all tables will be merged into.
- master_package_ = master_table_->CreatePackage(
- context_->GetCompilationPackage(), context_->GetPackageId());
+ master_package_ =
+ master_table_->CreatePackage(context_->GetCompilationPackage(), context_->GetPackageId());
CHECK(master_package_ != nullptr) << "package name or ID already taken";
}
-bool TableMerger::Merge(const Source& src, ResourceTable* table,
- io::IFileCollection* collection) {
- return MergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
+bool TableMerger::Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection) {
+ return MergeImpl(src, table, collection, false /*overlay*/, true /*allow_new*/);
}
bool TableMerger::MergeOverlay(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
- return MergeImpl(src, table, collection, true /* overlay */, options_.auto_add_overlay);
+ return MergeImpl(src, table, collection, true /*overlay*/, options_.auto_add_overlay);
}
-/**
- * This will merge packages with the same package name (or no package name).
- */
+// This will merge packages with the same package name (or no package name).
bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
- io::IFileCollection* collection, bool overlay,
- bool allow_new) {
+ io::IFileCollection* collection, bool overlay, bool allow_new) {
bool error = false;
for (auto& package : table->packages) {
// Only merge an empty package or the package we're building.
@@ -62,9 +58,8 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
if (package->name.empty() || context_->GetCompilationPackage() == package->name) {
FileMergeCallback callback;
if (collection) {
- callback = [&](const ResourceNameRef& name,
- const ConfigDescription& config, FileReference* new_file,
- FileReference* old_file) -> bool {
+ callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
+ FileReference* new_file, FileReference* old_file) -> bool {
// The old file's path points inside the APK, so we can use it as is.
io::IFile* f = collection->FindFile(*old_file->path);
if (!f) {
@@ -78,45 +73,38 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
};
}
- // Merge here. Once the entries are merged and mangled, any references to
- // them are still valid. This is because un-mangled references are
- // mangled, then looked up at resolution time.
- // Also, when linking, we convert references with no package name to use
- // the compilation package name.
- error |= !DoMerge(src, table, package.get(), false /* mangle */, overlay,
- allow_new, callback);
+ // Merge here. Once the entries are merged and mangled, any references to them are still
+ // valid. This is because un-mangled references are mangled, then looked up at resolution
+ // time. Also, when linking, we convert references with no package name to use the compilation
+ // package name.
+ error |=
+ !DoMerge(src, table, package.get(), false /* mangle */, overlay, allow_new, callback);
}
}
return !error;
}
-/**
- * This will merge and mangle resources from a static library.
- */
-bool TableMerger::MergeAndMangle(const Source& src,
- const StringPiece& package_name,
- ResourceTable* table,
- io::IFileCollection* collection) {
+// This will merge and mangle resources from a static library.
+bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_name,
+ ResourceTable* table, io::IFileCollection* collection) {
bool error = false;
for (auto& package : table->packages) {
// Warn of packages with an unrelated ID.
if (package_name != package->name) {
- context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package "
- << package->name);
+ context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package " << package->name);
continue;
}
bool mangle = package_name != context_->GetCompilationPackage();
merged_packages_.insert(package->name);
- auto callback = [&](
- const ResourceNameRef& name, const ConfigDescription& config,
- FileReference* new_file, FileReference* old_file) -> bool {
+ auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
+ FileReference* new_file, FileReference* old_file) -> bool {
// The old file's path points inside the APK, so we can use it as is.
io::IFile* f = collection->FindFile(*old_file->path);
if (!f) {
- context_->GetDiagnostics()->Error(
- DiagMessage(src) << "file '" << *old_file->path << "' not found");
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "file '" << *old_file->path << "' not found");
return false;
}
@@ -124,21 +112,18 @@ bool TableMerger::MergeAndMangle(const Source& src,
return true;
};
- error |= !DoMerge(src, table, package.get(), mangle, false /* overlay */,
- true /* allow new */, callback);
+ error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/,
+ callback);
}
return !error;
}
-static bool MergeType(IAaptContext* context, const Source& src,
- ResourceTableType* dst_type,
+static bool MergeType(IAaptContext* context, const Source& src, ResourceTableType* dst_type,
ResourceTableType* src_type) {
if (dst_type->symbol_status.state < src_type->symbol_status.state) {
- // The incoming type's visibility is stronger, so we should override
- // the visibility.
+ // The incoming type's visibility is stronger, so we should override the visibility.
if (src_type->symbol_status.state == SymbolState::kPublic) {
- // Only copy the ID if the source is public, or else the ID is
- // meaningless.
+ // Only copy the ID if the source is public, or else the ID is meaningless.
dst_type->id = src_type->id;
}
dst_type->symbol_status = std::move(src_type->symbol_status);
@@ -155,14 +140,12 @@ static bool MergeType(IAaptContext* context, const Source& src,
return true;
}
-static bool MergeEntry(IAaptContext* context, const Source& src,
- ResourceEntry* dst_entry, ResourceEntry* src_entry) {
+static bool MergeEntry(IAaptContext* context, const Source& src, ResourceEntry* dst_entry,
+ ResourceEntry* src_entry) {
if (dst_entry->symbol_status.state < src_entry->symbol_status.state) {
- // The incoming type's visibility is stronger, so we should override
- // the visibility.
+ // The incoming type's visibility is stronger, so we should override the visibility.
if (src_entry->symbol_status.state == SymbolState::kPublic) {
- // Only copy the ID if the source is public, or else the ID is
- // meaningless.
+ // Only copy the ID if the source is public, or else the ID is meaningless.
dst_entry->id = src_entry->id;
}
dst_entry->symbol_status = std::move(src_entry->symbol_status);
@@ -171,9 +154,8 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
dst_entry->id && src_entry->id &&
dst_entry->id.value() != src_entry->id.value()) {
// Both entries are public and have different IDs.
- context->GetDiagnostics()->Error(
- DiagMessage(src) << "cannot merge entry '" << src_entry->name
- << "': conflicting public IDs");
+ context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge entry '" << src_entry->name
+ << "': conflicting public IDs");
return false;
}
return true;
@@ -181,12 +163,10 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
// Modified CollisionResolver which will merge Styleables and Styles. Used with overlays.
//
-// Styleables are not actual resources, but they are treated as such during the
-// compilation phase.
+// Styleables are not actual resources, but they are treated as such during the compilation phase.
//
-// Styleables and Styles don't simply overlay each other, their definitions merge
-// and accumulate. If both values are Styleables/Styles, we just merge them into the
-// existing value.
+// Styleables and Styles don't simply overlay each other, their definitions merge and accumulate.
+// If both values are Styleables/Styles, we just merge them into the existing value.
static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Value* incoming,
StringPool* pool) {
if (Styleable* existing_styleable = ValueCast<Styleable>(existing)) {
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index c96b1b0b4dfb..81518ffb2441 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -33,81 +33,49 @@
namespace aapt {
struct TableMergerOptions {
- /**
- * If true, resources in overlays can be added without previously having
- * existed.
- */
+ // If true, resources in overlays can be added without previously having existed.
bool auto_add_overlay = false;
};
-/**
- * TableMerger takes resource tables and merges all packages within the tables
- * that have the same
- * package ID.
- *
- * If a package has a different name, all the entries in that table have their
- * names mangled
- * to include the package name. This way there are no collisions. In order to do
- * this correctly,
- * the TableMerger needs to also mangle any FileReference paths. Once these are
- * mangled,
- * the original source path of the file, along with the new destination path is
- * recorded in the
- * queue returned from getFileMergeQueue().
- *
- * Once the merging is complete, a separate process can go collect the files
- * from the various
- * source APKs and either copy or process their XML and put them in the correct
- * location in
- * the final APK.
- */
+// TableMerger takes resource tables and merges all packages within the tables that have the same
+// package ID.
+//
+// If a package has a different name, all the entries in that table have their names mangled
+// to include the package name. This way there are no collisions. In order to do this correctly,
+// the TableMerger needs to also mangle any FileReference paths. Once these are mangled, the
+// `IFile` pointer in `FileReference` will point to the original file.
+//
+// Once the merging is complete, a separate phase can go collect the files from the various
+// source APKs and either copy or process their XML and put them in the correct location in the
+// final APK.
class TableMerger {
public:
- /**
- * Note: The out_table ResourceTable must live longer than this TableMerger.
- * References are made to this ResourceTable for efficiency reasons.
- */
- TableMerger(IAaptContext* context, ResourceTable* out_table,
- const TableMergerOptions& options);
-
- const std::set<std::string>& merged_packages() const {
+ // Note: The out_table ResourceTable must live longer than this TableMerger.
+ // References are made to this ResourceTable for efficiency reasons.
+ TableMerger(IAaptContext* context, ResourceTable* out_table, const TableMergerOptions& options);
+
+ inline const std::set<std::string>& merged_packages() const {
return merged_packages_;
}
- /**
- * Merges resources from the same or empty package. This is for local sources.
- * An io::IFileCollection is optional and used to find the referenced Files
- * and process them.
- */
- bool Merge(const Source& src, ResourceTable* table,
- io::IFileCollection* collection = nullptr);
-
- /**
- * Merges resources from an overlay ResourceTable.
- * An io::IFileCollection is optional and used to find the referenced Files
- * and process them.
- */
+ // Merges resources from the same or empty package. This is for local sources.
+ // An io::IFileCollection is optional and used to find the referenced Files and process them.
+ bool Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection = nullptr);
+
+ // Merges resources from an overlay ResourceTable.
+ // An io::IFileCollection is optional and used to find the referenced Files and process them.
bool MergeOverlay(const Source& src, ResourceTable* table,
io::IFileCollection* collection = nullptr);
- /**
- * Merges resources from the given package, mangling the name. This is for
- * static libraries.
- * An io::IFileCollection is needed in order to find the referenced Files and
- * process them.
- */
+ // Merges resources from the given package, mangling the name. This is for static libraries.
+ // An io::IFileCollection is needed in order to find the referenced Files and process them.
bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table,
io::IFileCollection* collection);
- /**
- * Merges a compiled file that belongs to this same or empty package. This is
- * for local sources.
- */
+ // Merges a compiled file that belongs to this same or empty package. This is for local sources.
bool MergeFile(const ResourceFile& fileDesc, io::IFile* file);
- /**
- * Merges a compiled file from an overlay, overriding an existing definition.
- */
+ // Merges a compiled file from an overlay, overriding an existing definition.
bool MergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
private:
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index bcecd2003846..8852c8e8c66e 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -21,6 +21,7 @@
#include "Diagnostics.h"
#include "ResourceUtils.h"
#include "SdkConstants.h"
+#include "ValueVisitor.h"
#include "link/ReferenceLinker.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
@@ -31,16 +32,12 @@ namespace aapt {
namespace {
-/**
- * Visits all references (including parents of styles, references in styles,
- * arrays, etc) and
- * links their symbolic name to their Resource ID, performing mangling and
- * package aliasing
- * as needed.
- */
-class ReferenceVisitor : public ValueVisitor {
+// Visits all references (including parents of styles, references in styles, arrays, etc) and
+// links their symbolic name to their Resource ID, performing mangling and package aliasing
+// as needed.
+class ReferenceVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
ReferenceVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
xml::IPackageDeclStack* decls)
@@ -52,7 +49,9 @@ class ReferenceVisitor : public ValueVisitor {
}
}
- bool HasError() const { return error_; }
+ bool HasError() const {
+ return error_;
+ }
private:
DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
@@ -64,9 +63,7 @@ class ReferenceVisitor : public ValueVisitor {
bool error_;
};
-/**
- * Visits each xml Element and compiles the attributes within.
- */
+// Visits each xml Element and compiles the attributes within.
class XmlVisitor : public xml::PackageAwareVisitor {
public:
using xml::PackageAwareVisitor::Visit;
@@ -92,18 +89,12 @@ class XmlVisitor : public xml::PackageAwareVisitor {
// they were assigned to the default Attribute.
const Attribute* attribute = &kDefaultAttribute;
- std::string attribute_package;
if (Maybe<xml::ExtractedPackage> maybe_package =
xml::ExtractPackageFromNamespace(attr.namespace_uri)) {
// There is a valid package name for this attribute. We will look this up.
- attribute_package = maybe_package.value().package;
- if (attribute_package.empty()) {
- // Empty package means the 'current' or 'local' package.
- attribute_package = context_->GetCompilationPackage();
- }
-
- Reference attr_ref(ResourceNameRef(attribute_package, ResourceType::kAttr, attr.name));
+ Reference attr_ref(
+ ResourceNameRef(maybe_package.value().package, ResourceType::kAttr, attr.name));
attr_ref.private_reference = maybe_package.value().private_namespace;
std::string err_str;
@@ -111,9 +102,11 @@ class XmlVisitor : public xml::PackageAwareVisitor {
ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
if (!attr.compiled_attribute) {
- context_->GetDiagnostics()->Error(DiagMessage(source) << "attribute '"
- << attribute_package << ":"
- << attr.name << "' " << err_str);
+ DiagMessage error_msg(source);
+ error_msg << "attribute ";
+ ReferenceLinker::WriteAttributeName(attr_ref, callsite_, this, &error_msg);
+ error_msg << " " << err_str;
+ context_->GetDiagnostics()->Error(error_msg);
error_ = true;
continue;
}
@@ -129,12 +122,8 @@ class XmlVisitor : public xml::PackageAwareVisitor {
} else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
// We won't be able to encode this as a string.
DiagMessage msg(source);
- msg << "'" << attr.value << "' "
- << "is incompatible with attribute ";
- if (!attribute_package.empty()) {
- msg << attribute_package << ":";
- }
- msg << attr.name << " " << *attribute;
+ msg << "'" << attr.value << "' is incompatible with attribute " << attr.name << " "
+ << *attribute;
context_->GetDiagnostics()->Error(msg);
error_ = true;
}
@@ -163,7 +152,17 @@ class XmlVisitor : public xml::PackageAwareVisitor {
} // namespace
bool XmlReferenceLinker::Consume(IAaptContext* context, xml::XmlResource* resource) {
- const CallSite callsite = {resource->file.name};
+ CallSite callsite{resource->file.name.package};
+
+ std::string out_name = resource->file.name.entry;
+ NameMangler::Unmangle(&out_name, &callsite.package);
+
+ if (callsite.package.empty()) {
+ // Assume an empty package means that the XML file is local. This is true of AndroidManifest.xml
+ // for example.
+ callsite.package = context->GetCompilationPackage();
+ }
+
XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
if (resource->root) {
resource->root->Accept(&visitor);
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
new file mode 100644
index 000000000000..680308871af7
--- /dev/null
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -0,0 +1,355 @@
+/*
+ * 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.
+ */
+
+#include "MultiApkGenerator.h"
+
+#include <algorithm>
+#include <string>
+
+#include "androidfw/StringPiece.h"
+
+#include "LoadedApk.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
+#include "configuration/ConfigurationParser.h"
+#include "filter/AbiFilter.h"
+#include "filter/Filter.h"
+#include "format/Archive.h"
+#include "format/binary/XmlFlattener.h"
+#include "optimize/VersionCollapser.h"
+#include "process/IResourceTableConsumer.h"
+#include "split/TableSplitter.h"
+#include "util/Files.h"
+#include "xml/XmlDom.h"
+#include "xml/XmlUtil.h"
+
+namespace aapt {
+
+using ::aapt::configuration::AndroidSdk;
+using ::aapt::configuration::Artifact;
+using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::xml::kSchemaAndroid;
+using ::aapt::xml::XmlResource;
+using ::android::StringPiece;
+
+namespace {
+
+Maybe<AndroidSdk> GetAndroidSdk(const Artifact& artifact, const PostProcessingConfiguration& config,
+ IDiagnostics* diag) {
+ if (!artifact.android_sdk_group) {
+ return {};
+ }
+
+ const std::string& group_name = artifact.android_sdk_group.value();
+ auto group = config.android_sdk_groups.find(group_name);
+ // TODO: Remove validation when configuration parser ensures referential integrity.
+ if (group == config.android_sdk_groups.end()) {
+ diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
+ return {};
+ }
+
+ return group->second;
+}
+
+} // namespace
+
+/**
+ * Context wrapper that allows the min Android SDK value to be overridden.
+ */
+class ContextWrapper : public IAaptContext {
+ public:
+ explicit ContextWrapper(IAaptContext* context)
+ : context_(context), min_sdk_(context_->GetMinSdkVersion()) {
+ }
+
+ PackageType GetPackageType() override {
+ return context_->GetPackageType();
+ }
+
+ SymbolTable* GetExternalSymbols() override {
+ return context_->GetExternalSymbols();
+ }
+
+ IDiagnostics* GetDiagnostics() override {
+ return context_->GetDiagnostics();
+ }
+
+ const std::string& GetCompilationPackage() override {
+ return context_->GetCompilationPackage();
+ }
+
+ uint8_t GetPackageId() override {
+ return context_->GetPackageId();
+ }
+
+ NameMangler* GetNameMangler() override {
+ return context_->GetNameMangler();
+ }
+
+ bool IsVerbose() override {
+ return context_->IsVerbose();
+ }
+
+ int GetMinSdkVersion() override {
+ return min_sdk_;
+ }
+
+ void SetMinSdkVersion(int min_sdk) {
+ min_sdk_ = min_sdk;
+ }
+
+ private:
+ IAaptContext* context_;
+
+ int min_sdk_ = -1;
+};
+
+MultiApkGenerator::MultiApkGenerator(LoadedApk* apk, IAaptContext* context)
+ : apk_(apk), context_(context) {
+}
+
+bool MultiApkGenerator::FromBaseApk(const MultiApkGeneratorOptions& options) {
+ // TODO(safarmer): Handle APK version codes for the generated APKs.
+ IDiagnostics* diag = context_->GetDiagnostics();
+ const PostProcessingConfiguration& config = options.config;
+
+ const std::string& apk_name = file::GetFilename(apk_->GetSource().path).to_string();
+ const StringPiece ext = file::GetExtension(apk_name);
+ const std::string base_name = apk_name.substr(0, apk_name.rfind(ext.to_string()));
+
+ // For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
+ for (const Artifact& artifact : config.artifacts) {
+ FilterChain filters;
+
+ if (!artifact.name && !config.artifact_format) {
+ diag->Error(
+ DiagMessage() << "Artifact does not have a name and no global name template defined");
+ return false;
+ }
+
+ Maybe<std::string> artifact_name =
+ (artifact.name) ? artifact.Name(apk_name, diag)
+ : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
+
+ if (!artifact_name) {
+ diag->Error(DiagMessage() << "Could not determine split APK artifact name");
+ return false;
+ }
+
+ std::unique_ptr<ResourceTable> table =
+ FilterTable(artifact, config, *apk_->GetResourceTable(), &filters);
+ if (!table) {
+ return false;
+ }
+
+ std::unique_ptr<XmlResource> manifest;
+ if (!UpdateManifest(artifact, config, &manifest, diag)) {
+ diag->Error(DiagMessage() << "could not update AndroidManifest.xml for "
+ << artifact_name.value());
+ return false;
+ }
+
+ std::string out = options.out_dir;
+ if (!file::mkdirs(out)) {
+ context_->GetDiagnostics()->Warn(DiagMessage() << "could not create out dir: " << out);
+ }
+ file::AppendPath(&out, artifact_name.value());
+
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(DiagMessage() << "Generating split: " << out);
+ }
+
+ std::unique_ptr<IArchiveWriter> writer =
+ CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);
+
+ if (context_->IsVerbose()) {
+ diag->Note(DiagMessage() << "Writing output: " << out);
+ }
+
+ if (!apk_->WriteToArchive(context_, table.get(), options.table_flattener_options, &filters,
+ writer.get(), manifest.get())) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+std::unique_ptr<ResourceTable> MultiApkGenerator::FilterTable(
+ const configuration::Artifact& artifact,
+ const configuration::PostProcessingConfiguration& config, const ResourceTable& old_table,
+ FilterChain* filters) {
+ TableSplitterOptions splits;
+ AxisConfigFilter axis_filter;
+ ContextWrapper wrappedContext{context_};
+
+ if (artifact.abi_group) {
+ const std::string& group_name = artifact.abi_group.value();
+
+ auto group = config.abi_groups.find(group_name);
+ // TODO: Remove validation when configuration parser ensures referential integrity.
+ if (group == config.abi_groups.end()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced ABI group '"
+ << group_name << "'");
+ return {};
+ }
+ filters->AddFilter(AbiFilter::FromAbiList(group->second));
+ }
+
+ if (artifact.screen_density_group) {
+ const std::string& group_name = artifact.screen_density_group.value();
+
+ auto group = config.screen_density_groups.find(group_name);
+ // TODO: Remove validation when configuration parser ensures referential integrity.
+ if (group == config.screen_density_groups.end()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
+ << group_name << "'");
+ return {};
+ }
+
+ const std::vector<ConfigDescription>& densities = group->second;
+ for(const auto& density_config : densities) {
+ splits.preferred_densities.push_back(density_config.density);
+ }
+ }
+
+ if (artifact.locale_group) {
+ const std::string& group_name = artifact.locale_group.value();
+ auto group = config.locale_groups.find(group_name);
+ // TODO: Remove validation when configuration parser ensures referential integrity.
+ if (group == config.locale_groups.end()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
+ << group_name << "'");
+ return {};
+ }
+
+ const std::vector<ConfigDescription>& locales = group->second;
+ for (const auto& locale : locales) {
+ axis_filter.AddConfig(locale);
+ }
+ splits.config_filter = &axis_filter;
+ }
+
+ Maybe<AndroidSdk> sdk = GetAndroidSdk(artifact, config, context_->GetDiagnostics());
+ if (sdk && sdk.value().min_sdk_version) {
+ wrappedContext.SetMinSdkVersion(sdk.value().min_sdk_version.value());
+ }
+
+ std::unique_ptr<ResourceTable> table = old_table.Clone();
+
+ VersionCollapser collapser;
+ if (!collapser.Consume(&wrappedContext, table.get())) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "Failed to strip versioned resources");
+ return {};
+ }
+
+ TableSplitter splitter{{}, splits};
+ splitter.SplitTable(table.get());
+ return table;
+}
+
+bool MultiApkGenerator::UpdateManifest(const Artifact& artifact,
+ const PostProcessingConfiguration& config,
+ std::unique_ptr<XmlResource>* updated_manifest,
+ IDiagnostics* diag) {
+ *updated_manifest = apk_->InflateManifest(context_);
+ XmlResource* manifest = updated_manifest->get();
+ if (manifest == nullptr) {
+ return false;
+ }
+
+ // Make sure the first element is <manifest> with package attribute.
+ xml::Element* manifest_el = manifest->root.get();
+ if (manifest_el == nullptr) {
+ return false;
+ }
+
+ if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
+ diag->Error(DiagMessage(manifest->file.source) << "root tag must be <manifest>");
+ return false;
+ }
+
+ // Update the versionCode attribute.
+ xml::Attribute* versionCode = manifest_el->FindAttribute(kSchemaAndroid, "versionCode");
+ if (versionCode == nullptr) {
+ diag->Error(DiagMessage(manifest->file.source) << "manifest must have a versionCode attribute");
+ return false;
+ }
+
+ auto* compiled_version = ValueCast<BinaryPrimitive>(versionCode->compiled_value.get());
+ if (compiled_version == nullptr) {
+ diag->Error(DiagMessage(manifest->file.source) << "versionCode is invalid");
+ return false;
+ }
+
+ int new_version = compiled_version->value.data + artifact.version;
+ versionCode->compiled_value = ResourceUtils::TryParseInt(std::to_string(new_version));
+
+ // Check to see if the minSdkVersion needs to be updated.
+ Maybe<AndroidSdk> maybe_sdk = GetAndroidSdk(artifact, config, diag);
+ if (maybe_sdk) {
+ // TODO(safarmer): Handle the rest of the Android SDK.
+ const AndroidSdk& android_sdk = maybe_sdk.value();
+
+ if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
+ if (xml::Attribute* min_sdk_attr =
+ uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
+ // Populate with a pre-compiles attribute to we don't need to relink etc.
+ const std::string& min_sdk_str = std::to_string(android_sdk.min_sdk_version.value());
+ min_sdk_attr->compiled_value = ResourceUtils::TryParseInt(min_sdk_str);
+ } else {
+ // There was no minSdkVersion. This is strange since at this point we should have been
+ // through the manifest fixer which sets the default minSdkVersion.
+ diag->Error(DiagMessage(manifest->file.source) << "missing minSdkVersion from <uses-sdk>");
+ return false;
+ }
+ } else {
+ // No uses-sdk present. This is strange since at this point we should have been
+ // through the manifest fixer which should have added it.
+ diag->Error(DiagMessage(manifest->file.source) << "missing <uses-sdk> from <manifest>");
+ return false;
+ }
+ }
+
+ if (artifact.screen_density_group) {
+ auto densities = config.screen_density_groups.find(artifact.screen_density_group.value());
+ CHECK(densities != config.screen_density_groups.end()) << "Missing density group";
+
+ xml::Element* screens_el = manifest_el->FindChild({}, "compatible-screens");
+ if (!screens_el) {
+ // create a new element.
+ std::unique_ptr<xml::Element> new_screens_el = util::make_unique<xml::Element>();
+ new_screens_el->name = "compatible-screens";
+ screens_el = new_screens_el.get();
+ manifest_el->InsertChild(0, std::move(new_screens_el));
+ } else {
+ // clear out the old element.
+ screens_el->GetChildElements().clear();
+ }
+
+ for (const auto& density : densities->second) {
+ std::unique_ptr<xml::Element> screen_el = util::make_unique<xml::Element>();
+ screen_el->name = "screen";
+ const char* density_str = density.toString().string();
+ screen_el->attributes.push_back(xml::Attribute{kSchemaAndroid, "screenDensity", density_str});
+ screens_el->AppendChild(std::move(screen_el));
+ }
+ }
+
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h
new file mode 100644
index 000000000000..e6546eec584e
--- /dev/null
+++ b/tools/aapt2/optimize/MultiApkGenerator.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT2_APKSPLITTER_H
+#define AAPT2_APKSPLITTER_H
+
+#include "Diagnostics.h"
+#include "LoadedApk.h"
+#include "configuration/ConfigurationParser.h"
+
+namespace aapt {
+
+struct MultiApkGeneratorOptions {
+ std::string out_dir;
+ configuration::PostProcessingConfiguration config;
+ TableFlattenerOptions table_flattener_options;
+};
+
+/**
+ * Generates a set of APKs that are a subset of the original base APKs. Each of the new APKs contain
+ * only the resources and assets for an artifact in the configuration file.
+ */
+class MultiApkGenerator {
+ public:
+ MultiApkGenerator(LoadedApk* apk, IAaptContext* context);
+
+ /**
+ * Writes a set of APKs to the provided output directory. Each APK is a subset fo the base APK and
+ * represents an artifact in the post processing configuration.
+ */
+ bool FromBaseApk(const MultiApkGeneratorOptions& options);
+
+ protected:
+ virtual std::unique_ptr<ResourceTable> FilterTable(
+ const configuration::Artifact& artifact,
+ const configuration::PostProcessingConfiguration& config, const ResourceTable& old_table,
+ FilterChain* chain);
+
+ private:
+ IDiagnostics* GetDiagnostics() {
+ return context_->GetDiagnostics();
+ }
+
+ bool UpdateManifest(const configuration::Artifact& artifact,
+ const configuration::PostProcessingConfiguration& config,
+ std::unique_ptr<xml::XmlResource>* updated_manifest, IDiagnostics* diag);
+
+ LoadedApk* apk_;
+ IAaptContext* context_;
+};
+
+} // namespace aapt
+
+#endif // AAPT2_APKSPLITTER_H
diff --git a/tools/aapt2/optimize/MultiApkGenerator_test.cpp b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
new file mode 100644
index 000000000000..c8f3524748b6
--- /dev/null
+++ b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+#include "optimize/MultiApkGenerator.h"
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "LoadedApk.h"
+#include "ResourceTable.h"
+#include "configuration/ConfigurationParser.h"
+#include "filter/Filter.h"
+#include "format/Archive.h"
+#include "format/binary/TableFlattener.h"
+#include "process/IResourceTableConsumer.h"
+#include "test/Context.h"
+#include "test/Test.h"
+
+namespace aapt {
+namespace {
+
+using ::aapt::configuration::Abi;
+using ::aapt::configuration::AndroidSdk;
+using ::aapt::configuration::Artifact;
+using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::test::GetValue;
+using ::aapt::test::GetValueForConfig;
+using ::aapt::test::ParseConfigOrDie;
+using ::testing::Eq;
+using ::testing::IsNull;
+using ::testing::Not;
+using ::testing::NotNull;
+using ::testing::PrintToString;
+using ::testing::Return;
+using ::testing::Test;
+using ::testing::_;
+
+/**
+ * Subclass the MultiApkGenerator class so that we can access the protected FilterTable method to
+ * directly test table filter.
+ */
+class MultiApkGeneratorWrapper : public MultiApkGenerator {
+ public:
+ MultiApkGeneratorWrapper(LoadedApk* apk, IAaptContext* context)
+ : MultiApkGenerator(apk, context) {
+ }
+
+ std::unique_ptr<ResourceTable> FilterTable(
+ const configuration::Artifact& artifact,
+ const configuration::PostProcessingConfiguration& config, const ResourceTable& old_table,
+ FilterChain* pChain) override {
+ return MultiApkGenerator::FilterTable(artifact, config, old_table, pChain);
+ }
+};
+
+/** MultiApkGenerator test fixture. */
+class MultiApkGeneratorTest : public ::testing::Test {
+ public:
+ std::unique_ptr<ResourceTable> BuildTable() {
+ return test::ResourceTableBuilder()
+ .AddFileReference(kResourceName, "res/drawable-mdpi/icon.png", mdpi_)
+ .AddFileReference(kResourceName, "res/drawable-hdpi/icon.png", hdpi_)
+ .AddFileReference(kResourceName, "res/drawable-xhdpi/icon.png", xhdpi_)
+ .AddFileReference(kResourceName, "res/drawable-xxhdpi/icon.png", xxhdpi_)
+ .AddFileReference(kResourceName, "res/drawable-v19/icon.xml", v19_)
+ .AddFileReference(kResourceName, "res/drawable-v21/icon.xml", v21_)
+ .AddSimple("android:string/one")
+ .Build();
+ }
+
+ inline FileReference* ValueForConfig(ResourceTable* table, const ConfigDescription& config) {
+ return GetValueForConfig<FileReference>(table, kResourceName, config);
+ };
+
+ void SetUp() override {
+ }
+
+ protected:
+ static constexpr const char* kResourceName = "android:drawable/icon";
+
+ ConfigDescription default_ = ParseConfigOrDie("").CopyWithoutSdkVersion();
+ ConfigDescription mdpi_ = ParseConfigOrDie("mdpi").CopyWithoutSdkVersion();
+ ConfigDescription hdpi_ = ParseConfigOrDie("hdpi").CopyWithoutSdkVersion();
+ ConfigDescription xhdpi_ = ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion();
+ ConfigDescription xxhdpi_ = ParseConfigOrDie("xxhdpi").CopyWithoutSdkVersion();
+ ConfigDescription xxxhdpi_ = ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion();
+ ConfigDescription v19_ = ParseConfigOrDie("v19");
+ ConfigDescription v21_ = ParseConfigOrDie("v21");
+};
+
+TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) {
+ std::unique_ptr<ResourceTable> table = BuildTable();
+
+ LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
+ std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(19).Build();
+ PostProcessingConfiguration empty_config;
+ TableFlattenerOptions table_flattener_options;
+ FilterChain chain;
+
+ Artifact x64 = test::ArtifactBuilder()
+ .SetName("${basename}.${sdk}.apk")
+ .SetDensityGroup("xhdpi")
+ .SetAndroidSdk("v23")
+ .Build();
+
+ auto config = test::PostProcessingConfigurationBuilder()
+ .SetLocaleGroup("en", {"en"})
+ .SetAbiGroup("x64", {Abi::kX86_64})
+ .SetDensityGroup("xhdpi", {"xhdpi"})
+ .SetAndroidSdk("v23", AndroidSdk::ForMinSdk(23))
+ .AddArtifact(x64)
+ .Build();
+
+ MultiApkGeneratorWrapper generator{&apk, ctx.get()};
+ std::unique_ptr<ResourceTable> split =
+ generator.FilterTable(x64, config, *apk.GetResourceTable(), &chain);
+
+ ResourceTable* new_table = split.get();
+ EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, hdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, xxhdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, xxxhdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, v19_), IsNull());
+
+ // xhdpi directly matches one of the required dimensions.
+ EXPECT_THAT(ValueForConfig(new_table, xhdpi_), NotNull());
+ // drawable-v21 was converted to drawable.
+ EXPECT_THAT(ValueForConfig(new_table, default_), NotNull());
+ EXPECT_THAT(GetValue<Id>(new_table, "android:string/one"), NotNull());
+}
+
+TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) {
+ std::unique_ptr<ResourceTable> table = BuildTable();
+
+ LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
+ std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
+ PostProcessingConfiguration empty_config;
+ TableFlattenerOptions table_flattener_options;
+ FilterChain chain;
+
+ Artifact x64 = test::ArtifactBuilder()
+ .SetName("${basename}.${sdk}.apk")
+ .SetDensityGroup("xhdpi")
+ .SetAndroidSdk("v4")
+ .Build();
+
+ auto config = test::PostProcessingConfigurationBuilder()
+ .SetLocaleGroup("en", {"en"})
+ .SetAbiGroup("x64", {Abi::kX86_64})
+ .SetDensityGroup("xhdpi", {"xhdpi"})
+ .SetAndroidSdk("v4", AndroidSdk::ForMinSdk(4))
+ .AddArtifact(x64)
+ .Build();
+
+ MultiApkGeneratorWrapper generator{&apk, ctx.get()};;
+ std::unique_ptr<ResourceTable> split =
+ generator.FilterTable(x64, config, *apk.GetResourceTable(), &chain);
+
+ ResourceTable* new_table = split.get();
+ EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, hdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, xxhdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, xxxhdpi_), IsNull());
+
+ EXPECT_THAT(ValueForConfig(new_table, xhdpi_), NotNull());
+ EXPECT_THAT(ValueForConfig(new_table, v19_), NotNull());
+ EXPECT_THAT(ValueForConfig(new_table, v21_), NotNull());
+ EXPECT_THAT(GetValue<Id>(new_table, "android:string/one"), NotNull());
+}
+
+TEST_F(MultiApkGeneratorTest, VersionFilterNoVersion) {
+ std::unique_ptr<ResourceTable> table = BuildTable();
+
+ LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
+ std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
+ PostProcessingConfiguration empty_config;
+ TableFlattenerOptions table_flattener_options;
+ FilterChain chain;
+
+ Artifact x64 =
+ test::ArtifactBuilder().SetName("${basename}.${sdk}.apk").SetDensityGroup("xhdpi").Build();
+
+ auto config = test::PostProcessingConfigurationBuilder()
+ .SetLocaleGroup("en", {"en"})
+ .SetAbiGroup("x64", {Abi::kX86_64})
+ .SetDensityGroup("xhdpi", {"xhdpi"})
+ .AddArtifact(x64)
+ .Build();
+
+ MultiApkGeneratorWrapper generator{&apk, ctx.get()};
+ std::unique_ptr<ResourceTable> split =
+ generator.FilterTable(x64, config, *apk.GetResourceTable(), &chain);
+
+ ResourceTable* new_table = split.get();
+ EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, hdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, xxhdpi_), IsNull());
+ EXPECT_THAT(ValueForConfig(new_table, xxxhdpi_), IsNull());
+
+ EXPECT_THAT(ValueForConfig(new_table, xhdpi_), NotNull());
+ EXPECT_THAT(ValueForConfig(new_table, v19_), NotNull());
+ EXPECT_THAT(ValueForConfig(new_table, v21_), NotNull());
+ EXPECT_THAT(GetValue<Id>(new_table, "android:string/one"), NotNull());
+}
+
+} // namespace
+} // namespace aapt
diff --git a/tools/aapt2/optimize/VersionCollapser.cpp b/tools/aapt2/optimize/VersionCollapser.cpp
index d941b487e439..cc1fc1e6910b 100644
--- a/tools/aapt2/optimize/VersionCollapser.cpp
+++ b/tools/aapt2/optimize/VersionCollapser.cpp
@@ -63,11 +63,9 @@ FilterIterator<Iterator, Pred> make_filter_iterator(Iterator begin,
}
/**
- * Every Configuration with an SDK version specified that is less than minSdk
- * will be removed.
- * The exception is when there is no exact matching resource for the minSdk. The
- * next smallest
- * one will be kept.
+ * Every Configuration with an SDK version specified that is less than minSdk will be removed. The
+ * exception is when there is no exact matching resource for the minSdk. The next smallest one will
+ * be kept.
*/
static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
// First look for all sdks less than minSdk.
@@ -80,11 +78,9 @@ static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
const ConfigDescription& config = (*iter)->config;
if (config.sdkVersion <= min_sdk) {
- // This is the first configuration we've found with a smaller or equal SDK
- // level
- // to the minimum. We MUST keep this one, but remove all others we find,
- // which get
- // overridden by this one.
+ // This is the first configuration we've found with a smaller or equal SDK level to the
+ // minimum. We MUST keep this one, but remove all others we find, which get overridden by this
+ // one.
ConfigDescription config_without_sdk = config.CopyWithoutSdkVersion();
auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
@@ -115,11 +111,9 @@ static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
-> bool { return val == nullptr; }),
entry->values.end());
- // Strip the version qualifiers for every resource with version <= minSdk.
- // This will ensure
- // that the resource entries are all packed together in the same ResTable_type
- // struct
- // and take up less space in the resources.arsc table.
+ // Strip the version qualifiers for every resource with version <= minSdk. This will ensure that
+ // the resource entries are all packed together in the same ResTable_type struct and take up less
+ // space in the resources.arsc table.
bool modified = false;
for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
if (config_value->config.sdkVersion != 0 &&
@@ -137,8 +131,8 @@ static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
}
if (modified) {
- // We've modified the keys (ConfigDescription) by changing the sdkVersion to
- // 0. We MUST re-sort to ensure ordering guarantees hold.
+ // We've modified the keys (ConfigDescription) by changing the sdkVersion to 0. We MUST re-sort
+ // to ensure ordering guarantees hold.
std::sort(entry->values.begin(), entry->values.end(),
[](const std::unique_ptr<ResourceConfigValue>& a,
const std::unique_ptr<ResourceConfigValue>& b) -> bool {
diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp
deleted file mode 100644
index aa99c982f6ae..000000000000
--- a/tools/aapt2/proto/ProtoHelpers.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2016 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 "proto/ProtoHelpers.h"
-
-namespace aapt {
-
-void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool) {
- BigBuffer buffer(1024);
- StringPool::FlattenUtf8(&buffer, pool);
-
- std::string* data = out_pb_pool->mutable_data();
- data->reserve(buffer.size());
-
- size_t offset = 0;
- for (const BigBuffer::Block& block : buffer) {
- data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
- offset += block.size;
- }
-}
-
-void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source) {
- StringPool::Ref ref = src_pool->MakeRef(source.path);
- out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index()));
- if (source.line) {
- out_pb_source->mutable_position()->set_line_number(static_cast<uint32_t>(source.line.value()));
- }
-}
-
-void DeserializeSourceFromPb(const pb::Source& pb_source, const android::ResStringPool& src_pool,
- Source* out_source) {
- if (pb_source.has_path_idx()) {
- out_source->path = util::GetString(src_pool, pb_source.path_idx());
- }
-
- if (pb_source.has_position()) {
- out_source->line = static_cast<size_t>(pb_source.position().line_number());
- }
-}
-
-pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state) {
- switch (state) {
- case SymbolState::kPrivate:
- return pb::SymbolStatus_Visibility_PRIVATE;
- case SymbolState::kPublic:
- return pb::SymbolStatus_Visibility_PUBLIC;
- default:
- break;
- }
- return pb::SymbolStatus_Visibility_UNKNOWN;
-}
-
-SymbolState DeserializeVisibilityFromPb(pb::SymbolStatus_Visibility pb_visibility) {
- switch (pb_visibility) {
- case pb::SymbolStatus_Visibility_PRIVATE:
- return SymbolState::kPrivate;
- case pb::SymbolStatus_Visibility_PUBLIC:
- return SymbolState::kPublic;
- default:
- break;
- }
- return SymbolState::kUndefined;
-}
-
-void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config) {
- android::ResTable_config flat_config = config;
- flat_config.size = sizeof(flat_config);
- flat_config.swapHtoD();
- out_pb_config->set_data(&flat_config, sizeof(flat_config));
-}
-
-bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
- ConfigDescription* out_config) {
- if (!pb_config.has_data()) {
- return false;
- }
-
- const android::ResTable_config* config;
- if (pb_config.data().size() > sizeof(*config)) {
- return false;
- }
-
- config = reinterpret_cast<const android::ResTable_config*>(pb_config.data().data());
- out_config->copyFromDtoH(*config);
- return true;
-}
-
-pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
- switch (type) {
- case Reference::Type::kResource:
- return pb::Reference_Type_REFERENCE;
- case Reference::Type::kAttribute:
- return pb::Reference_Type_ATTRIBUTE;
- default:
- break;
- }
- return pb::Reference_Type_REFERENCE;
-}
-
-Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type) {
- switch (pb_type) {
- case pb::Reference_Type_REFERENCE:
- return Reference::Type::kResource;
- case pb::Reference_Type_ATTRIBUTE:
- return Reference::Type::kAttribute;
- default:
- break;
- }
- return Reference::Type::kResource;
-}
-
-pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
- switch (plural_idx) {
- case Plural::Zero:
- return pb::Plural_Arity_ZERO;
- case Plural::One:
- return pb::Plural_Arity_ONE;
- case Plural::Two:
- return pb::Plural_Arity_TWO;
- case Plural::Few:
- return pb::Plural_Arity_FEW;
- case Plural::Many:
- return pb::Plural_Arity_MANY;
- default:
- break;
- }
- return pb::Plural_Arity_OTHER;
-}
-
-size_t DeserializePluralEnumFromPb(pb::Plural_Arity arity) {
- switch (arity) {
- case pb::Plural_Arity_ZERO:
- return Plural::Zero;
- case pb::Plural_Arity_ONE:
- return Plural::One;
- case pb::Plural_Arity_TWO:
- return Plural::Two;
- case pb::Plural_Arity_FEW:
- return Plural::Few;
- case pb::Plural_Arity_MANY:
- return Plural::Many;
- default:
- break;
- }
- return Plural::Other;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/proto/ProtoHelpers.h b/tools/aapt2/proto/ProtoHelpers.h
deleted file mode 100644
index 2f268f44752c..000000000000
--- a/tools/aapt2/proto/ProtoHelpers.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 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_PROTO_PROTOHELPERS_H
-#define AAPT_PROTO_PROTOHELPERS_H
-
-#include "androidfw/ResourceTypes.h"
-
-#include "ConfigDescription.h"
-#include "ResourceTable.h"
-#include "Source.h"
-#include "StringPool.h"
-#include "Resources.pb.h"
-#include "ResourcesInternal.pb.h"
-
-namespace aapt {
-
-void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool);
-
-void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source);
-
-void DeserializeSourceFromPb(const pb::Source& pb_source, const android::ResStringPool& src_pool,
- Source* out_source);
-
-pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state);
-
-SymbolState DeserializeVisibilityFromPb(pb::SymbolStatus_Visibility pb_visibility);
-
-void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config);
-
-bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
- ConfigDescription* out_config);
-
-pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type);
-
-Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type);
-
-pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx);
-
-size_t DeserializePluralEnumFromPb(pb::Plural_Arity arity);
-
-} // namespace aapt
-
-#endif /* AAPT_PROTO_PROTOHELPERS_H */
diff --git a/tools/aapt2/proto/ProtoSerialize.h b/tools/aapt2/proto/ProtoSerialize.h
deleted file mode 100644
index 8c46642e9090..000000000000
--- a/tools/aapt2/proto/ProtoSerialize.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2016 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_FLATTEN_TABLEPROTOSERIALIZER_H
-#define AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
-
-#include "android-base/macros.h"
-#include "google/protobuf/io/coded_stream.h"
-#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
-
-#include "Diagnostics.h"
-#include "ResourceTable.h"
-#include "Source.h"
-#include "proto/ProtoHelpers.h"
-
-namespace aapt {
-
-class CompiledFileOutputStream {
- public:
- explicit CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out);
-
- void WriteLittleEndian32(uint32_t value);
- void WriteCompiledFile(const pb::internal::CompiledFile* compiledFile);
- void WriteData(const BigBuffer* buffer);
- void WriteData(const void* data, size_t len);
- bool HadError();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
-
- void EnsureAlignedWrite();
-
- google::protobuf::io::CodedOutputStream out_;
-};
-
-class CompiledFileInputStream {
- public:
- explicit CompiledFileInputStream(const void* data, size_t size);
-
- bool ReadLittleEndian32(uint32_t* outVal);
- bool ReadCompiledFile(pb::internal::CompiledFile* outVal);
- bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
-
- void EnsureAlignedRead();
-
- google::protobuf::io::CodedInputStream in_;
-};
-
-std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table);
-std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pbTable,
- const Source& source, IDiagnostics* diag);
-
-std::unique_ptr<pb::internal::CompiledFile> SerializeCompiledFileToPb(const ResourceFile& file);
-std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
- const pb::internal::CompiledFile& pbFile, const Source& source, IDiagnostics* diag);
-
-} // namespace aapt
-
-#endif /* AAPT_FLATTEN_TABLEPROTOSERIALIZER_H */
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
deleted file mode 100644
index b9d5878f2f71..000000000000
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Copyright (C) 2016 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 "proto/ProtoSerialize.h"
-
-#include "android-base/logging.h"
-#include "androidfw/ResourceTypes.h"
-
-#include "ResourceTable.h"
-#include "ResourceUtils.h"
-#include "ValueVisitor.h"
-#include "proto/ProtoHelpers.h"
-
-namespace aapt {
-
-namespace {
-
-class ReferenceIdToNameVisitor : public ValueVisitor {
- public:
- using ValueVisitor::Visit;
-
- explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping)
- : mapping_(mapping) {
- CHECK(mapping_ != nullptr);
- }
-
- void Visit(Reference* reference) override {
- if (!reference->id || !reference->id.value().is_valid()) {
- return;
- }
-
- ResourceId id = reference->id.value();
- auto cache_iter = mapping_->find(id);
- if (cache_iter != mapping_->end()) {
- reference->name = cache_iter->second.ToResourceName();
- }
- }
-
- private:
- const std::map<ResourceId, ResourceNameRef>* mapping_;
-};
-
-class PackagePbDeserializer {
- public:
- PackagePbDeserializer(const android::ResStringPool* sourcePool, const Source& source,
- IDiagnostics* diag)
- : source_pool_(sourcePool), source_(source), diag_(diag) {
- }
-
- public:
- bool DeserializeFromPb(const pb::Package& pb_package, ResourceTable* table) {
- Maybe<uint8_t> id;
- if (pb_package.has_package_id()) {
- id = static_cast<uint8_t>(pb_package.package_id());
- }
-
- std::map<ResourceId, ResourceNameRef> id_index;
-
- ResourceTablePackage* pkg = table->CreatePackage(pb_package.package_name(), id);
- for (const pb::Type& pb_type : pb_package.type()) {
- const ResourceType* res_type = ParseResourceType(pb_type.name());
- if (res_type == nullptr) {
- diag_->Error(DiagMessage(source_) << "unknown type '" << pb_type.name() << "'");
- return {};
- }
-
- ResourceTableType* type = pkg->FindOrCreateType(*res_type);
-
- for (const pb::Entry& pb_entry : pb_type.entry()) {
- ResourceEntry* entry = type->FindOrCreateEntry(pb_entry.name());
-
- // Deserialize the symbol status (public/private with source and comments).
- if (pb_entry.has_symbol_status()) {
- const pb::SymbolStatus& pb_status = pb_entry.symbol_status();
- if (pb_status.has_source()) {
- DeserializeSourceFromPb(pb_status.source(), *source_pool_,
- &entry->symbol_status.source);
- }
-
- if (pb_status.has_comment()) {
- entry->symbol_status.comment = pb_status.comment();
- }
-
- entry->symbol_status.allow_new = pb_status.allow_new();
-
- SymbolState visibility = DeserializeVisibilityFromPb(pb_status.visibility());
- entry->symbol_status.state = visibility;
-
- if (visibility == SymbolState::kPublic) {
- // This is a public symbol, we must encode the ID now if there is one.
- if (pb_entry.has_id()) {
- entry->id = static_cast<uint16_t>(pb_entry.id());
- }
-
- if (type->symbol_status.state != SymbolState::kPublic) {
- // If the type has not been made public, do so now.
- type->symbol_status.state = SymbolState::kPublic;
- if (pb_type.has_id()) {
- type->id = static_cast<uint8_t>(pb_type.id());
- }
- }
- } else if (visibility == SymbolState::kPrivate) {
- if (type->symbol_status.state == SymbolState::kUndefined) {
- type->symbol_status.state = SymbolState::kPrivate;
- }
- }
- }
-
- ResourceId resid(pb_package.package_id(), pb_type.id(), pb_entry.id());
- if (resid.is_valid()) {
- id_index[resid] = ResourceNameRef(pkg->name, type->type, entry->name);
- }
-
- for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) {
- const pb::ConfigDescription& pb_config = pb_config_value.config();
-
- ConfigDescription config;
- if (!DeserializeConfigDescriptionFromPb(pb_config, &config)) {
- diag_->Error(DiagMessage(source_) << "invalid configuration");
- return {};
- }
-
- ResourceConfigValue* config_value = entry->FindOrCreateValue(config, pb_config.product());
- if (config_value->value) {
- // Duplicate config.
- diag_->Error(DiagMessage(source_) << "duplicate configuration");
- return {};
- }
-
- config_value->value =
- DeserializeValueFromPb(pb_config_value.value(), config, &table->string_pool);
- if (!config_value->value) {
- return {};
- }
- }
- }
- }
-
- ReferenceIdToNameVisitor visitor(&id_index);
- VisitAllValuesInPackage(pkg, &visitor);
- return true;
- }
-
- private:
- std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
- const ConfigDescription& config, StringPool* pool) {
- if (pb_item.has_ref()) {
- const pb::Reference& pb_ref = pb_item.ref();
- std::unique_ptr<Reference> ref = util::make_unique<Reference>();
- if (!DeserializeReferenceFromPb(pb_ref, ref.get())) {
- return {};
- }
- return std::move(ref);
-
- } else if (pb_item.has_prim()) {
- const pb::Primitive& pb_prim = pb_item.prim();
- return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()),
- pb_prim.data());
-
- } else if (pb_item.has_id()) {
- return util::make_unique<Id>();
-
- } else if (pb_item.has_str()) {
- return util::make_unique<String>(
- pool->MakeRef(pb_item.str().value(), StringPool::Context(config)));
-
- } else if (pb_item.has_raw_str()) {
- return util::make_unique<RawString>(
- pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config)));
-
- } else if (pb_item.has_styled_str()) {
- const pb::StyledString& pb_str = pb_item.styled_str();
- StyleString style_str{pb_str.value()};
- for (const pb::StyledString::Span& pb_span : pb_str.span()) {
- style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()});
- }
- return util::make_unique<StyledString>(pool->MakeRef(
- style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
-
- } else if (pb_item.has_file()) {
- return util::make_unique<FileReference>(pool->MakeRef(
- pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
-
- } else {
- diag_->Error(DiagMessage(source_) << "unknown item");
- }
- return {};
- }
-
- std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
- const ConfigDescription& config,
- StringPool* pool) {
- std::unique_ptr<Value> value;
- if (pb_value.has_item()) {
- value = DeserializeItemFromPb(pb_value.item(), config, pool);
- if (!value) {
- return {};
- }
-
- } else if (pb_value.has_compound_value()) {
- const pb::CompoundValue& pb_compound_value = pb_value.compound_value();
- if (pb_compound_value.has_attr()) {
- const pb::Attribute& pb_attr = pb_compound_value.attr();
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>();
- attr->type_mask = pb_attr.format_flags();
- attr->min_int = pb_attr.min_int();
- attr->max_int = pb_attr.max_int();
- for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbol()) {
- Attribute::Symbol symbol;
- DeserializeItemCommon(pb_symbol, &symbol.symbol);
- if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol)) {
- return {};
- }
- symbol.value = pb_symbol.value();
- attr->symbols.push_back(std::move(symbol));
- }
- value = std::move(attr);
-
- } else if (pb_compound_value.has_style()) {
- const pb::Style& pb_style = pb_compound_value.style();
- std::unique_ptr<Style> style = util::make_unique<Style>();
- if (pb_style.has_parent()) {
- style->parent = Reference();
- if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value())) {
- return {};
- }
-
- if (pb_style.has_parent_source()) {
- Source parent_source;
- DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_, &parent_source);
- style->parent.value().SetSource(std::move(parent_source));
- }
- }
-
- for (const pb::Style_Entry& pb_entry : pb_style.entry()) {
- Style::Entry entry;
- DeserializeItemCommon(pb_entry, &entry.key);
- if (!DeserializeReferenceFromPb(pb_entry.key(), &entry.key)) {
- return {};
- }
-
- entry.value = DeserializeItemFromPb(pb_entry.item(), config, pool);
- if (!entry.value) {
- return {};
- }
-
- DeserializeItemCommon(pb_entry, entry.value.get());
- style->entries.push_back(std::move(entry));
- }
- value = std::move(style);
-
- } else if (pb_compound_value.has_styleable()) {
- const pb::Styleable& pb_styleable = pb_compound_value.styleable();
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
- for (const pb::Styleable_Entry& pb_entry : pb_styleable.entry()) {
- Reference attr_ref;
- DeserializeItemCommon(pb_entry, &attr_ref);
- DeserializeReferenceFromPb(pb_entry.attr(), &attr_ref);
- styleable->entries.push_back(std::move(attr_ref));
- }
- value = std::move(styleable);
-
- } else if (pb_compound_value.has_array()) {
- const pb::Array& pb_array = pb_compound_value.array();
- std::unique_ptr<Array> array = util::make_unique<Array>();
- for (const pb::Array_Element& pb_entry : pb_array.element()) {
- std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), config, pool);
- if (!item) {
- return {};
- }
-
- DeserializeItemCommon(pb_entry, item.get());
- array->elements.push_back(std::move(item));
- }
- value = std::move(array);
-
- } else if (pb_compound_value.has_plural()) {
- const pb::Plural& pb_plural = pb_compound_value.plural();
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- for (const pb::Plural_Entry& pb_entry : pb_plural.entry()) {
- size_t pluralIdx = DeserializePluralEnumFromPb(pb_entry.arity());
- plural->values[pluralIdx] = DeserializeItemFromPb(pb_entry.item(), config, pool);
- if (!plural->values[pluralIdx]) {
- return {};
- }
-
- DeserializeItemCommon(pb_entry, plural->values[pluralIdx].get());
- }
- value = std::move(plural);
-
- } else {
- diag_->Error(DiagMessage(source_) << "unknown compound value");
- return {};
- }
- } else {
- diag_->Error(DiagMessage(source_) << "unknown value");
- return {};
- }
-
- CHECK(value) << "forgot to set value";
-
- value->SetWeak(pb_value.weak());
- DeserializeItemCommon(pb_value, value.get());
- return value;
- }
-
- bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* out_ref) {
- out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
- out_ref->private_reference = pb_ref.private_();
-
- if (pb_ref.has_id()) {
- out_ref->id = ResourceId(pb_ref.id());
- }
-
- if (pb_ref.has_name()) {
- ResourceNameRef name_ref;
- if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) {
- diag_->Error(DiagMessage(source_) << "invalid reference name '" << pb_ref.name() << "'");
- return false;
- }
-
- out_ref->name = name_ref.ToResourceName();
- }
- return true;
- }
-
- template <typename T>
- void DeserializeItemCommon(const T& pb_item, Value* out_value) {
- if (pb_item.has_source()) {
- Source source;
- DeserializeSourceFromPb(pb_item.source(), *source_pool_, &source);
- out_value->SetSource(std::move(source));
- }
-
- if (pb_item.has_comment()) {
- out_value->SetComment(pb_item.comment());
- }
- }
-
- private:
- const android::ResStringPool* source_pool_;
- const Source source_;
- IDiagnostics* diag_;
-};
-
-} // namespace
-
-std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pb_table,
- const Source& source, IDiagnostics* diag) {
- // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
- // causes errors when qualifying it with android::
- using namespace android;
-
- std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
-
- ResStringPool source_pool;
- if (pb_table.has_source_pool()) {
- status_t result = source_pool.setTo(pb_table.source_pool().data().data(),
- pb_table.source_pool().data().size());
- if (result != NO_ERROR) {
- diag->Error(DiagMessage(source) << "invalid source pool");
- return {};
- }
- }
-
- PackagePbDeserializer package_pb_deserializer(&source_pool, source, diag);
- for (const pb::Package& pb_package : pb_table.package()) {
- if (!package_pb_deserializer.DeserializeFromPb(pb_package, table.get())) {
- return {};
- }
- }
- return table;
-}
-
-std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
- const pb::internal::CompiledFile& pb_file, const Source& source, IDiagnostics* diag) {
- std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
-
- ResourceNameRef name_ref;
-
- // Need to create an lvalue here so that nameRef can point to something real.
- if (!ResourceUtils::ParseResourceName(pb_file.resource_name(), &name_ref)) {
- diag->Error(DiagMessage(source)
- << "invalid resource name in compiled file header: "
- << pb_file.resource_name());
- return {};
- }
- file->name = name_ref.ToResourceName();
- file->source.path = pb_file.source_path();
- DeserializeConfigDescriptionFromPb(pb_file.config(), &file->config);
-
- for (const pb::internal::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbol()) {
- // Need to create an lvalue here so that nameRef can point to something real.
- if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(), &name_ref)) {
- diag->Error(DiagMessage(source)
- << "invalid resource name for exported symbol in "
- "compiled file header: "
- << pb_file.resource_name());
- return {};
- }
- size_t line = 0u;
- if (pb_symbol.has_source()) {
- line = pb_symbol.source().line_number();
- }
- file->exported_symbols.push_back(SourcedResourceName{name_ref.ToResourceName(), line});
- }
- return file;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
deleted file mode 100644
index a08df71eae1e..000000000000
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Copyright (C) 2016 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 "Resource.h"
-#include "ResourceTable.h"
-#include "StringPool.h"
-#include "ValueVisitor.h"
-#include "proto/ProtoHelpers.h"
-#include "proto/ProtoSerialize.h"
-#include "util/BigBuffer.h"
-
-#include "android-base/logging.h"
-
-using ::google::protobuf::io::CodedInputStream;
-using ::google::protobuf::io::CodedOutputStream;
-using ::google::protobuf::io::ZeroCopyOutputStream;
-
-namespace aapt {
-
-namespace {
-
-class PbSerializerVisitor : public RawValueVisitor {
- public:
- using RawValueVisitor::Visit;
-
- // Constructor to use when expecting to serialize any value.
- PbSerializerVisitor(StringPool* source_pool, pb::Value* out_pb_value)
- : source_pool_(source_pool), out_pb_value_(out_pb_value), out_pb_item_(nullptr) {
- }
-
- // Constructor to use when expecting to serialize an Item.
- PbSerializerVisitor(StringPool* sourcePool, pb::Item* outPbItem)
- : source_pool_(sourcePool), out_pb_value_(nullptr), out_pb_item_(outPbItem) {
- }
-
- void Visit(Reference* ref) override {
- SerializeReferenceToPb(*ref, pb_item()->mutable_ref());
- }
-
- void Visit(String* str) override {
- pb_item()->mutable_str()->set_value(*str->value);
- }
-
- void Visit(RawString* str) override {
- pb_item()->mutable_raw_str()->set_value(*str->value);
- }
-
- void Visit(StyledString* str) override {
- pb::StyledString* pb_str = pb_item()->mutable_styled_str();
- pb_str->set_value(str->value->value);
-
- for (const StringPool::Span& span : str->value->spans) {
- pb::StyledString::Span* pb_span = pb_str->add_span();
- pb_span->set_tag(*span.name);
- pb_span->set_first_char(span.first_char);
- pb_span->set_last_char(span.last_char);
- }
- }
-
- void Visit(FileReference* file) override {
- pb_item()->mutable_file()->set_path(*file->path);
- }
-
- void Visit(Id* /*id*/) override {
- pb_item()->mutable_id();
- }
-
- void Visit(BinaryPrimitive* prim) override {
- android::Res_value val = {};
- prim->Flatten(&val);
-
- pb::Primitive* pb_prim = pb_item()->mutable_prim();
- pb_prim->set_type(val.dataType);
- pb_prim->set_data(val.data);
- }
-
- void VisitItem(Item* item) override {
- LOG(FATAL) << "unimplemented item";
- }
-
- void Visit(Attribute* attr) override {
- pb::Attribute* pb_attr = pb_compound_value()->mutable_attr();
- pb_attr->set_format_flags(attr->type_mask);
- pb_attr->set_min_int(attr->min_int);
- pb_attr->set_max_int(attr->max_int);
-
- for (auto& symbol : attr->symbols) {
- pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbol();
- SerializeItemCommonToPb(symbol.symbol, pb_symbol);
- SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
- pb_symbol->set_value(symbol.value);
- }
- }
-
- void Visit(Style* style) override {
- pb::Style* pb_style = pb_compound_value()->mutable_style();
- if (style->parent) {
- SerializeReferenceToPb(style->parent.value(), pb_style->mutable_parent());
- SerializeSourceToPb(style->parent.value().GetSource(), source_pool_,
- pb_style->mutable_parent_source());
- }
-
- for (Style::Entry& entry : style->entries) {
- pb::Style_Entry* pb_entry = pb_style->add_entry();
- SerializeReferenceToPb(entry.key, pb_entry->mutable_key());
-
- pb::Item* pb_item = pb_entry->mutable_item();
- SerializeItemCommonToPb(entry.key, pb_entry);
- PbSerializerVisitor sub_visitor(source_pool_, pb_item);
- entry.value->Accept(&sub_visitor);
- }
- }
-
- void Visit(Styleable* styleable) override {
- pb::Styleable* pb_styleable = pb_compound_value()->mutable_styleable();
- for (Reference& entry : styleable->entries) {
- pb::Styleable_Entry* pb_entry = pb_styleable->add_entry();
- SerializeItemCommonToPb(entry, pb_entry);
- SerializeReferenceToPb(entry, pb_entry->mutable_attr());
- }
- }
-
- void Visit(Array* array) override {
- pb::Array* pb_array = pb_compound_value()->mutable_array();
- for (auto& value : array->elements) {
- pb::Array_Element* pb_element = pb_array->add_element();
- SerializeItemCommonToPb(*value, pb_element);
- PbSerializerVisitor sub_visitor(source_pool_, pb_element->mutable_item());
- value->Accept(&sub_visitor);
- }
- }
-
- void Visit(Plural* plural) override {
- pb::Plural* pb_plural = pb_compound_value()->mutable_plural();
- const size_t count = plural->values.size();
- for (size_t i = 0; i < count; i++) {
- if (!plural->values[i]) {
- // No plural value set here.
- continue;
- }
-
- pb::Plural_Entry* pb_entry = pb_plural->add_entry();
- pb_entry->set_arity(SerializePluralEnumToPb(i));
- pb::Item* pb_element = pb_entry->mutable_item();
- SerializeItemCommonToPb(*plural->values[i], pb_entry);
- PbSerializerVisitor sub_visitor(source_pool_, pb_element);
- plural->values[i]->Accept(&sub_visitor);
- }
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PbSerializerVisitor);
-
- pb::Item* pb_item() {
- if (out_pb_value_) {
- return out_pb_value_->mutable_item();
- }
- return out_pb_item_;
- }
-
- pb::CompoundValue* pb_compound_value() {
- CHECK(out_pb_value_ != nullptr);
- return out_pb_value_->mutable_compound_value();
- }
-
- template <typename T>
- void SerializeItemCommonToPb(const Item& item, T* pb_item) {
- SerializeSourceToPb(item.GetSource(), source_pool_, pb_item->mutable_source());
- if (!item.GetComment().empty()) {
- pb_item->set_comment(item.GetComment());
- }
- }
-
- void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
- if (ref.id) {
- pb_ref->set_id(ref.id.value().id);
- }
-
- if (ref.name) {
- pb_ref->set_name(ref.name.value().ToString());
- }
-
- pb_ref->set_private_(ref.private_reference);
- pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
- }
-
- StringPool* source_pool_;
- pb::Value* out_pb_value_;
- pb::Item* out_pb_item_;
-};
-
-} // namespace
-
-std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
- // We must do this before writing the resources, since the string pool IDs may change.
- table->string_pool.Prune();
- table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
- int diff = util::compare(a.priority, b.priority);
- if (diff == 0) {
- diff = a.config.compare(b.config);
- }
- return diff;
- });
-
- auto pb_table = util::make_unique<pb::ResourceTable>();
- StringPool source_pool;
-
- for (auto& package : table->packages) {
- pb::Package* pb_package = pb_table->add_package();
- if (package->id) {
- pb_package->set_package_id(package->id.value());
- }
- pb_package->set_package_name(package->name);
-
- for (auto& type : package->types) {
- pb::Type* pb_type = pb_package->add_type();
- if (type->id) {
- pb_type->set_id(type->id.value());
- }
- pb_type->set_name(ToString(type->type).to_string());
-
- for (auto& entry : type->entries) {
- pb::Entry* pb_entry = pb_type->add_entry();
- if (entry->id) {
- pb_entry->set_id(entry->id.value());
- }
- pb_entry->set_name(entry->name);
-
- // Write the SymbolStatus struct.
- pb::SymbolStatus* pb_status = pb_entry->mutable_symbol_status();
- pb_status->set_visibility(SerializeVisibilityToPb(entry->symbol_status.state));
- SerializeSourceToPb(entry->symbol_status.source, &source_pool, pb_status->mutable_source());
- pb_status->set_comment(entry->symbol_status.comment);
- pb_status->set_allow_new(entry->symbol_status.allow_new);
-
- for (auto& config_value : entry->values) {
- pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
- SerializeConfig(config_value->config, pb_config_value->mutable_config());
- if (!config_value->product.empty()) {
- pb_config_value->mutable_config()->set_product(config_value->product);
- }
-
- pb::Value* pb_value = pb_config_value->mutable_value();
- SerializeSourceToPb(config_value->value->GetSource(), &source_pool,
- pb_value->mutable_source());
- if (!config_value->value->GetComment().empty()) {
- pb_value->set_comment(config_value->value->GetComment());
- }
-
- if (config_value->value->IsWeak()) {
- pb_value->set_weak(true);
- }
-
- PbSerializerVisitor visitor(&source_pool, pb_value);
- config_value->value->Accept(&visitor);
- }
- }
- }
- }
-
- SerializeStringPoolToPb(source_pool, pb_table->mutable_source_pool());
- return pb_table;
-}
-
-std::unique_ptr<pb::internal::CompiledFile> SerializeCompiledFileToPb(const ResourceFile& file) {
- auto pb_file = util::make_unique<pb::internal::CompiledFile>();
- pb_file->set_resource_name(file.name.ToString());
- pb_file->set_source_path(file.source.path);
- SerializeConfig(file.config, pb_file->mutable_config());
-
- for (const SourcedResourceName& exported : file.exported_symbols) {
- pb::internal::CompiledFile_Symbol* pb_symbol = pb_file->add_exported_symbol();
- pb_symbol->set_resource_name(exported.name.ToString());
- pb_symbol->mutable_source()->set_line_number(exported.line);
- }
- return pb_file;
-}
-
-CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : out_(out) {
-}
-
-void CompiledFileOutputStream::EnsureAlignedWrite() {
- const int padding = out_.ByteCount() % 4;
- if (padding > 0) {
- uint32_t zero = 0u;
- out_.WriteRaw(&zero, padding);
- }
-}
-
-void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian32(val);
-}
-
-void CompiledFileOutputStream::WriteCompiledFile(const pb::internal::CompiledFile* compiled_file) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian64(static_cast<uint64_t>(compiled_file->ByteSize()));
- compiled_file->SerializeWithCachedSizes(&out_);
-}
-
-void CompiledFileOutputStream::WriteData(const BigBuffer* buffer) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian64(static_cast<uint64_t>(buffer->size()));
- for (const BigBuffer::Block& block : *buffer) {
- out_.WriteRaw(block.buffer.get(), block.size);
- }
-}
-
-void CompiledFileOutputStream::WriteData(const void* data, size_t len) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian64(static_cast<uint64_t>(len));
- out_.WriteRaw(data, len);
-}
-
-bool CompiledFileOutputStream::HadError() {
- return out_.HadError();
-}
-
-CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size)
- : in_(static_cast<const uint8_t*>(data), size) {}
-
-void CompiledFileInputStream::EnsureAlignedRead() {
- const int padding = in_.CurrentPosition() % 4;
- if (padding > 0) {
- // Reads are always 4 byte aligned.
- in_.Skip(padding);
- }
-}
-
-bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* out_val) {
- EnsureAlignedRead();
- return in_.ReadLittleEndian32(out_val);
-}
-
-bool CompiledFileInputStream::ReadCompiledFile(pb::internal::CompiledFile* out_val) {
- EnsureAlignedRead();
-
- google::protobuf::uint64 pb_size = 0u;
- if (!in_.ReadLittleEndian64(&pb_size)) {
- return false;
- }
-
- CodedInputStream::Limit l = in_.PushLimit(static_cast<int>(pb_size));
-
- // Check that we haven't tried to read past the end.
- if (static_cast<uint64_t>(in_.BytesUntilLimit()) != pb_size) {
- in_.PopLimit(l);
- in_.PushLimit(0);
- return false;
- }
-
- if (!out_val->ParsePartialFromCodedStream(&in_)) {
- in_.PopLimit(l);
- in_.PushLimit(0);
- return false;
- }
-
- in_.PopLimit(l);
- return true;
-}
-
-bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset, uint64_t* out_len) {
- EnsureAlignedRead();
-
- google::protobuf::uint64 pb_size = 0u;
- if (!in_.ReadLittleEndian64(&pb_size)) {
- return false;
- }
-
- // Check that we aren't trying to read past the end.
- if (pb_size > static_cast<uint64_t>(in_.BytesUntilLimit())) {
- in_.PushLimit(0);
- return false;
- }
-
- uint64_t offset = static_cast<uint64_t>(in_.CurrentPosition());
- if (!in_.Skip(pb_size)) {
- return false;
- }
-
- *out_offset = offset;
- *out_len = pb_size;
- return true;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
deleted file mode 100644
index 80608b3d9c05..000000000000
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2016 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 "proto/ProtoSerialize.h"
-
-#include "ResourceTable.h"
-#include "test/Test.h"
-
-using ::google::protobuf::io::StringOutputStream;
-using ::testing::Eq;
-using ::testing::NotNull;
-using ::testing::SizeIs;
-
-namespace aapt {
-
-TEST(TableProtoSerializer, SerializeSinglePackage) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<ResourceTable> table =
- test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
- .AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000), "res/layout/main.xml")
- .AddReference("com.app.a:layout/other", ResourceId(0x7f020001), "com.app.a:layout/main")
- .AddString("com.app.a:string/text", {}, "hi")
- .AddValue("com.app.a:id/foo", {}, util::make_unique<Id>())
- .SetSymbolState("com.app.a:bool/foo", {}, SymbolState::kUndefined, true /*allow_new*/)
- .Build();
-
- Symbol public_symbol;
- public_symbol.state = SymbolState::kPublic;
- ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"),
- ResourceId(0x7f020000), public_symbol,
- context->GetDiagnostics()));
-
- Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
- ASSERT_THAT(id, NotNull());
-
- // Make a plural.
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- plural->values[Plural::One] = util::make_unique<String>(table->string_pool.MakeRef("one"));
- ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:plurals/hey"),
- ConfigDescription{}, {}, std::move(plural),
- context->GetDiagnostics()));
-
- // Make a styled string.
- StyleString style_string;
- style_string.str = "hello";
- style_string.spans.push_back(Span{"b", 0u, 4u});
- ASSERT_TRUE(
- table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {},
- util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)),
- context->GetDiagnostics()));
-
- // Make a resource with different products.
- ASSERT_TRUE(table->AddResource(
- test::ParseNameOrDie("com.app.a:integer/one"),
- test::ParseConfigOrDie("land"), {},
- test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u),
- context->GetDiagnostics()));
- ASSERT_TRUE(table->AddResource(
- test::ParseNameOrDie("com.app.a:integer/one"),
- test::ParseConfigOrDie("land"), "tablet",
- test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 321u),
- context->GetDiagnostics()));
-
- // Make a reference with both resource name and resource ID.
- // The reference should point to a resource outside of this table to test that both name and id
- // get serialized.
- Reference expected_ref;
- expected_ref.name = test::ParseNameOrDie("android:layout/main");
- expected_ref.id = ResourceId(0x01020000);
- ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:layout/abc"),
- ConfigDescription::DefaultConfig(), {},
- util::make_unique<Reference>(expected_ref),
- context->GetDiagnostics()));
-
- std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table.get());
- ASSERT_THAT(pb_table, NotNull());
-
- std::unique_ptr<ResourceTable> new_table = DeserializeTableFromPb(
- *pb_table, Source{"test"}, context->GetDiagnostics());
- ASSERT_THAT(new_table, NotNull());
-
- Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo");
- ASSERT_THAT(new_id, NotNull());
- EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
-
- Maybe<ResourceTable::SearchResult> result =
- new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
- ASSERT_TRUE(result);
-
- EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic));
- EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
-
- result = new_table->FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
- ASSERT_TRUE(result);
- EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined));
- EXPECT_TRUE(result.value().entry->symbol_status.allow_new);
-
- // Find the product-dependent values
- BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
- new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "");
- ASSERT_THAT(prim, NotNull());
- EXPECT_THAT(prim->value.data, Eq(123u));
-
- prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
- new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
- ASSERT_THAT(prim, NotNull());
- EXPECT_THAT(prim->value.data, Eq(321u));
-
- Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc");
- ASSERT_THAT(actual_ref, NotNull());
- ASSERT_TRUE(actual_ref->name);
- ASSERT_TRUE(actual_ref->id);
- EXPECT_THAT(*actual_ref, Eq(expected_ref));
-
- StyledString* actual_styled_str =
- test::GetValue<StyledString>(new_table.get(), "com.app.a:string/styled");
- ASSERT_THAT(actual_styled_str, NotNull());
- EXPECT_THAT(actual_styled_str->value->value, Eq("hello"));
- ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u));
- EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b"));
- EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
- EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
-}
-
-TEST(TableProtoSerializer, SerializeFileHeader) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- ResourceFile f;
- f.config = test::ParseConfigOrDie("hdpi-v9");
- f.name = test::ParseNameOrDie("com.app.a:layout/main");
- f.source.path = "res/layout-hdpi-v9/main.xml";
- f.exported_symbols.push_back(
- SourcedResourceName{test::ParseNameOrDie("id/unchecked"), 23u});
-
- const std::string expected_data1 = "123";
- const std::string expected_data2 = "1234";
-
- std::string output_str;
- {
- std::unique_ptr<pb::internal::CompiledFile> pb_file1 = SerializeCompiledFileToPb(f);
-
- f.name.entry = "__" + f.name.entry + "$0";
- std::unique_ptr<pb::internal::CompiledFile> pb_file2 = SerializeCompiledFileToPb(f);
-
- StringOutputStream out_stream(&output_str);
- CompiledFileOutputStream out_file_stream(&out_stream);
- out_file_stream.WriteLittleEndian32(2);
- out_file_stream.WriteCompiledFile(pb_file1.get());
- out_file_stream.WriteData(expected_data1.data(), expected_data1.size());
- out_file_stream.WriteCompiledFile(pb_file2.get());
- out_file_stream.WriteData(expected_data2.data(), expected_data2.size());
- ASSERT_FALSE(out_file_stream.HadError());
- }
-
- CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
- uint32_t num_files = 0;
- ASSERT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
- ASSERT_EQ(2u, num_files);
-
- // Read the first compiled file.
-
- pb::internal::CompiledFile new_pb_file;
- ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file));
-
- std::unique_ptr<ResourceFile> file = DeserializeCompiledFileFromPb(
- new_pb_file, Source("test"), context->GetDiagnostics());
- ASSERT_THAT(file, NotNull());
-
- uint64_t offset, len;
- ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
-
- std::string actual_data(output_str.data() + offset, len);
- EXPECT_EQ(expected_data1, actual_data);
-
- // Expect the data to be aligned.
- EXPECT_EQ(0u, offset & 0x03);
-
- ASSERT_EQ(1u, file->exported_symbols.size());
- EXPECT_EQ(test::ParseNameOrDie("id/unchecked"), file->exported_symbols[0].name);
-
- // Read the second compiled file.
-
- ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file));
-
- file = DeserializeCompiledFileFromPb(new_pb_file, Source("test"), context->GetDiagnostics());
- ASSERT_THAT(file, NotNull());
-
- ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
-
- actual_data = std::string(output_str.data() + offset, len);
- EXPECT_EQ(expected_data2, actual_data);
-
- // Expect the data to be aligned.
- EXPECT_EQ(0u, offset & 0x03);
-}
-
-TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) {
- ResourceFile f;
- std::unique_ptr<pb::internal::CompiledFile> pb_file = SerializeCompiledFileToPb(f);
-
- const std::string expected_data = "1234";
-
- std::string output_str;
- {
- StringOutputStream out_stream(&output_str);
- CompiledFileOutputStream out_file_stream(&out_stream);
- out_file_stream.WriteLittleEndian32(1);
- out_file_stream.WriteCompiledFile(pb_file.get());
- out_file_stream.WriteData(expected_data.data(), expected_data.size());
- ASSERT_FALSE(out_file_stream.HadError());
- }
-
- output_str[4] = 0xff;
-
- CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
-
- uint32_t num_files = 0;
- EXPECT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
- EXPECT_EQ(1u, num_files);
-
- pb::internal::CompiledFile new_pb_file;
- EXPECT_FALSE(in_file_stream.ReadCompiledFile(&new_pb_file));
-
- uint64_t offset, len;
- EXPECT_FALSE(in_file_stream.ReadDataMetaData(&offset, &len));
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 27e13d81ff49..9d49ca6c0aa9 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -32,8 +32,7 @@
namespace aapt {
using ConfigClaimedMap = std::unordered_map<ResourceConfigValue*, bool>;
-using ConfigDensityGroups =
- std::map<ConfigDescription, std::vector<ResourceConfigValue*>>;
+using ConfigDensityGroups = std::map<ConfigDescription, std::vector<ResourceConfigValue*>>;
static ConfigDescription CopyWithoutDensity(const ConfigDescription& config) {
ConfigDescription without_density = config;
@@ -51,8 +50,7 @@ class SplitValueSelector {
if (config.density == 0) {
density_independent_configs_.insert(config);
} else {
- density_dependent_config_to_density_map_[CopyWithoutDensity(config)] =
- config.density;
+ density_dependent_config_to_density_map_[CopyWithoutDensity(config)] = config.density;
}
}
}
@@ -94,9 +92,7 @@ class SplitValueSelector {
ResourceConfigValue* best_value = nullptr;
for (ResourceConfigValue* this_value : related_values) {
- if (!best_value ||
- this_value->config.isBetterThan(best_value->config,
- &target_density)) {
+ if (!best_value || this_value->config.isBetterThan(best_value->config, &target_density)) {
best_value = this_value;
}
}
@@ -120,9 +116,8 @@ class SplitValueSelector {
};
/**
- * Marking non-preferred densities as claimed will make sure the base doesn't
- * include them,
- * leaving only the preferred density behind.
+ * Marking non-preferred densities as claimed will make sure the base doesn't include them, leaving
+ * only the preferred density behind.
*/
static void MarkNonPreferredDensitiesAsClaimed(
const std::vector<uint16_t>& preferred_densities, const ConfigDensityGroups& density_groups,
@@ -161,8 +156,7 @@ bool TableSplitter::VerifySplitConstraints(IAaptContext* context) {
for (size_t i = 0; i < split_constraints_.size(); i++) {
for (size_t j = i + 1; j < split_constraints_.size(); j++) {
for (const ConfigDescription& config : split_constraints_[i].configs) {
- if (split_constraints_[j].configs.find(config) !=
- split_constraints_[j].configs.end()) {
+ if (split_constraints_[j].configs.find(config) != split_constraints_[j].configs.end()) {
context->GetDiagnostics()->Error(DiagMessage()
<< "config '" << config
<< "' appears in multiple splits, "
@@ -193,28 +187,22 @@ void TableSplitter::SplitTable(ResourceTable* original_table) {
for (auto& entry : type->entries) {
if (options_.config_filter) {
// First eliminate any resource that we definitely don't want.
- for (std::unique_ptr<ResourceConfigValue>& config_value :
- entry->values) {
+ for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
if (!options_.config_filter->Match(config_value->config)) {
- // null out the entry. We will clean up and remove nulls at the
- // end for performance reasons.
+ // null out the entry. We will clean up and remove nulls at the end for performance
+ // reasons.
config_value.reset();
}
}
}
- // Organize the values into two separate buckets. Those that are
- // density-dependent
- // and those that are density-independent.
- // One density technically matches all density, it's just that some
- // densities
- // match better. So we need to be aware of the full set of densities to
- // make this
- // decision.
+ // Organize the values into two separate buckets. Those that are density-dependent and those
+ // that are density-independent. One density technically matches all density, it's just that
+ // some densities match better. So we need to be aware of the full set of densities to make
+ // this decision.
ConfigDensityGroups density_groups;
ConfigClaimedMap config_claimed_map;
- for (const std::unique_ptr<ResourceConfigValue>& config_value :
- entry->values) {
+ for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
if (config_value) {
config_claimed_map[config_value.get()] = false;
@@ -226,9 +214,8 @@ void TableSplitter::SplitTable(ResourceTable* original_table) {
}
}
- // First we check all the splits. If it doesn't match one of the splits,
- // we
- // leave it in the base.
+ // First we check all the splits. If it doesn't match one of the splits, we leave it in the
+ // base.
for (size_t idx = 0; idx < split_count; idx++) {
const SplitConstraints& split_constraint = split_constraints_[idx];
ResourceTable* split_table = splits_[idx].get();
@@ -240,20 +227,16 @@ void TableSplitter::SplitTable(ResourceTable* original_table) {
// No need to do any work if we selected nothing.
if (!selected_values.empty()) {
- // Create the same resource structure in the split. We do this
- // lazily because we might not have actual values for each
- // type/entry.
- ResourceTablePackage* split_pkg =
- split_table->FindPackage(pkg->name);
- ResourceTableType* split_type =
- split_pkg->FindOrCreateType(type->type);
+ // Create the same resource structure in the split. We do this lazily because we might
+ // not have actual values for each type/entry.
+ ResourceTablePackage* split_pkg = split_table->FindPackage(pkg->name);
+ ResourceTableType* split_type = split_pkg->FindOrCreateType(type->type);
if (!split_type->id) {
split_type->id = type->id;
split_type->symbol_status = type->symbol_status;
}
- ResourceEntry* split_entry =
- split_type->FindOrCreateEntry(entry->name);
+ ResourceEntry* split_entry = split_type->FindOrCreateEntry(entry->name);
if (!split_entry->id) {
split_entry->id = entry->id;
split_entry->symbol_status = entry->symbol_status;
@@ -262,8 +245,7 @@ void TableSplitter::SplitTable(ResourceTable* original_table) {
// Copy the selected values into the new Split Entry.
for (ResourceConfigValue* config_value : selected_values) {
ResourceConfigValue* new_config_value =
- split_entry->FindOrCreateValue(config_value->config,
- config_value->product);
+ split_entry->FindOrCreateValue(config_value->config, config_value->product);
new_config_value->value = std::unique_ptr<Value>(
config_value->value->Clone(&split_table->string_pool));
}
@@ -276,11 +258,9 @@ void TableSplitter::SplitTable(ResourceTable* original_table) {
&config_claimed_map);
}
- // All splits are handled, now check to see what wasn't claimed and
- // remove
- // whatever exists in other splits.
- for (std::unique_ptr<ResourceConfigValue>& config_value :
- entry->values) {
+ // All splits are handled, now check to see what wasn't claimed and remove whatever exists
+ // in other splits.
+ for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
if (config_value && config_claimed_map[config_value.get()]) {
// Claimed, remove from base.
config_value.reset();
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 8f9788e8a25d..e658664d8653 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -23,6 +23,9 @@
#include "test/Common.h"
#include "util/Util.h"
+using ::aapt::configuration::Abi;
+using ::aapt::configuration::AndroidSdk;
+using ::aapt::configuration::Artifact;
using ::aapt::io::StringInputStream;
using ::android::StringPiece;
@@ -212,5 +215,74 @@ std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* contex
return doc;
}
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::SetAbiGroup(
+ const std::string& name, const std::vector<Abi>& abis) {
+ config_.abi_groups[name] = abis;
+ return *this;
+}
+
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::SetLocaleGroup(
+ const std::string& name, const std::vector<std::string>& locales) {
+ auto& group = config_.locale_groups[name];
+ for (const auto& locale : locales) {
+ group.push_back(ParseConfigOrDie(locale));
+ }
+ return *this;
+}
+
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::SetDensityGroup(
+ const std::string& name, const std::vector<std::string>& densities) {
+ auto& group = config_.screen_density_groups[name];
+ for (const auto& density : densities) {
+ group.push_back(ParseConfigOrDie(density));
+ }
+ return *this;
+}
+
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::SetAndroidSdk(
+ const std::string& name, const AndroidSdk& sdk) {
+ config_.android_sdk_groups[name] = sdk;
+ return *this;
+}
+
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddArtifact(
+ const Artifact& artifact) {
+ config_.artifacts.push_back(artifact);
+ return *this;
+}
+
+configuration::PostProcessingConfiguration PostProcessingConfigurationBuilder::Build() {
+ return config_;
+}
+
+ArtifactBuilder& ArtifactBuilder::SetName(const std::string& name) {
+ artifact_.name = {name};
+ return *this;
+}
+
+ArtifactBuilder& ArtifactBuilder::SetAbiGroup(const std::string& name) {
+ artifact_.abi_group = {name};
+ return *this;
+}
+
+ArtifactBuilder& ArtifactBuilder::SetDensityGroup(const std::string& name) {
+ artifact_.screen_density_group = {name};
+ return *this;
+}
+
+ArtifactBuilder& ArtifactBuilder::SetLocaleGroup(const std::string& name) {
+ artifact_.locale_group = {name};
+ return *this;
+}
+
+ArtifactBuilder& ArtifactBuilder::SetAndroidSdk(const std::string& name) {
+ artifact_.android_sdk_group = {name};
+ return *this;
+}
+
+configuration::Artifact ArtifactBuilder::Build() {
+ return artifact_;
+}
+
} // namespace test
} // namespace aapt
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index d9f3912fb4c6..263fb5562b25 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -24,7 +24,9 @@
#include "Resource.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
+#include "configuration/ConfigurationParser.h"
#include "process/IResourceTableConsumer.h"
+#include "test/Common.h"
#include "util/Maybe.h"
#include "xml/XmlDom.h"
@@ -149,6 +151,40 @@ std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str);
std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* context,
const android::StringPiece& str);
+class PostProcessingConfigurationBuilder {
+ public:
+ PostProcessingConfigurationBuilder() = default;
+
+ PostProcessingConfigurationBuilder& SetAbiGroup(const std::string& name,
+ const std::vector<configuration::Abi>& abis);
+ PostProcessingConfigurationBuilder& SetLocaleGroup(const std::string& name,
+ const std::vector<std::string>& locales);
+ PostProcessingConfigurationBuilder& SetDensityGroup(const std::string& name,
+ const std::vector<std::string>& densities);
+ PostProcessingConfigurationBuilder& SetAndroidSdk(const std::string& name,
+ const configuration::AndroidSdk& sdk);
+ PostProcessingConfigurationBuilder& AddArtifact(const configuration::Artifact& artifact);
+ configuration::PostProcessingConfiguration Build();
+
+ private:
+ configuration::PostProcessingConfiguration config_;
+};
+
+class ArtifactBuilder {
+ public:
+ ArtifactBuilder() = default;
+
+ ArtifactBuilder& SetName(const std::string& name);
+ ArtifactBuilder& SetAbiGroup(const std::string& name);
+ ArtifactBuilder& SetDensityGroup(const std::string& name);
+ ArtifactBuilder& SetLocaleGroup(const std::string& name);
+ ArtifactBuilder& SetAndroidSdk(const std::string& name);
+ configuration::Artifact Build();
+
+ private:
+ configuration::Artifact artifact_;
+};
+
} // namespace test
} // namespace aapt
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index e6b38c0007b4..61d056309a69 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -41,13 +41,13 @@ IDiagnostics* GetDiagnostics();
inline ResourceName ParseNameOrDie(const android::StringPiece& str) {
ResourceNameRef ref;
- CHECK(ResourceUtils::ParseResourceName(str, &ref)) << "invalid resource name";
+ CHECK(ResourceUtils::ParseResourceName(str, &ref)) << "invalid resource name: " << str;
return ref.ToResourceName();
}
inline ConfigDescription ParseConfigOrDie(const android::StringPiece& str) {
ConfigDescription config;
- CHECK(ConfigDescription::Parse(str, &config)) << "invalid configuration";
+ CHECK(ConfigDescription::Parse(str, &config)) << "invalid configuration: " << str;
return config;
}
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 51a75d7556ad..a9b49d9d9904 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -520,11 +520,10 @@ bool Tokenizer::iterator::operator!=(const iterator& rhs) const {
return !(*this == rhs);
}
-Tokenizer::iterator::iterator(StringPiece s, char sep, StringPiece tok,
- bool end)
+Tokenizer::iterator::iterator(const StringPiece& s, char sep, const StringPiece& tok, bool end)
: str_(s), separator_(sep), token_(tok), end_(end) {}
-Tokenizer::Tokenizer(StringPiece str, char sep)
+Tokenizer::Tokenizer(const StringPiece& str, char sep)
: begin_(++iterator(str, sep, StringPiece(str.begin() - 1, 0), false)),
end_(str, sep, StringPiece(str.end(), 0), true) {}
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 8f021ab8cb8a..c928458786e4 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -246,7 +246,7 @@ class Tokenizer {
private:
friend class Tokenizer;
- iterator(android::StringPiece s, char sep, android::StringPiece tok, bool end);
+ iterator(const android::StringPiece& s, char sep, const android::StringPiece& tok, bool end);
android::StringPiece str_;
char separator_;
@@ -254,7 +254,7 @@ class Tokenizer {
bool end_;
};
- Tokenizer(android::StringPiece str, char sep);
+ Tokenizer(const android::StringPiece& str, char sep);
iterator begin() const {
return begin_;
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 1d122db9990f..b3e0a926b4bd 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -236,16 +236,22 @@ static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPoo
attr.name = util::Utf16ToUtf8(StringPiece16(str16, len));
}
+ uint32_t res_id = parser->getAttributeNameResID(i);
+ if (res_id > 0) {
+ attr.compiled_attribute = AaptAttribute(::aapt::Attribute(), {res_id});
+ }
+
str16 = parser->getAttributeStringValue(i, &len);
if (str16) {
attr.value = util::Utf16ToUtf8(StringPiece16(str16, len));
+ } else {
+ android::Res_value res_value;
+ if (parser->getAttributeValue(i, &res_value) > 0) {
+ attr.compiled_value = ResourceUtils::ParseBinaryResValue(
+ ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
+ }
}
- android::Res_value res_value;
- if (parser->getAttributeValue(i, &res_value) > 0) {
- attr.compiled_value = ResourceUtils::ParseBinaryResValue(
- ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
- }
el->attributes.push_back(std::move(attr));
}
@@ -455,6 +461,12 @@ void Element::Accept(Visitor* visitor) {
visitor->AfterVisitElement(this);
}
+void Element::Accept(ConstVisitor* visitor) const {
+ visitor->BeforeVisitElement(this);
+ visitor->Visit(this);
+ visitor->AfterVisitElement(this);
+}
+
std::unique_ptr<Node> Text::Clone(const ElementCloneFunc&) const {
auto t = util::make_unique<Text>();
t->comment = comment;
@@ -468,6 +480,10 @@ void Text::Accept(Visitor* visitor) {
visitor->Visit(this);
}
+void Text::Accept(ConstVisitor* visitor) const {
+ visitor->Visit(this);
+}
+
void PackageAwareVisitor::BeforeVisitElement(Element* el) {
std::vector<PackageDecl> decls;
for (const NamespaceDecl& decl : el->namespace_decls) {
@@ -482,10 +498,9 @@ void PackageAwareVisitor::AfterVisitElement(Element* el) {
package_decls_.pop_back();
}
-Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
- const StringPiece& alias, const StringPiece& local_package) const {
+Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(const StringPiece& alias) const {
if (alias.empty()) {
- return ExtractedPackage{local_package.to_string(), false /* private */};
+ return ExtractedPackage{{}, false /*private*/};
}
const auto rend = package_decls_.rend();
@@ -496,7 +511,7 @@ Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
const PackageDecl& decl = *iter2;
if (alias == decl.prefix) {
if (decl.package.package.empty()) {
- return ExtractedPackage{local_package.to_string(), decl.package.private_namespace};
+ return ExtractedPackage{{}, decl.package.private_namespace};
}
return decl.package;
}
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 154224381626..063d7b9210c1 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -35,6 +35,7 @@ namespace xml {
class Element;
class Visitor;
+class ConstVisitor;
// Base class for all XML nodes.
class Node {
@@ -47,6 +48,7 @@ class Node {
std::string comment;
virtual void Accept(Visitor* visitor) = 0;
+ virtual void Accept(ConstVisitor* visitor) const = 0;
using ElementCloneFunc = std::function<void(const Element&, Element*)>;
@@ -112,6 +114,7 @@ class Element : public Node {
std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) const override;
void Accept(Visitor* visitor) override;
+ void Accept(ConstVisitor* visitor) const override;
};
// A Text (CDATA) XML node. Can not have any children.
@@ -122,6 +125,7 @@ class Text : public Node {
std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) const override;
void Accept(Visitor* visitor) override;
+ void Accept(ConstVisitor* visitor) const override;
};
// An XML resource with a source, name, and XML tree.
@@ -180,13 +184,44 @@ class Visitor {
friend class Element;
};
+class ConstVisitor {
+ public:
+ virtual ~ConstVisitor() = default;
+
+ virtual void Visit(const Element* el) {
+ VisitChildren(el);
+ }
+
+ virtual void Visit(const Text* text) {
+ }
+
+ protected:
+ ConstVisitor() = default;
+
+ void VisitChildren(const Element* el) {
+ for (const auto& child : el->children) {
+ child->Accept(this);
+ }
+ }
+
+ virtual void BeforeVisitElement(const Element* el) {
+ }
+
+ virtual void AfterVisitElement(const Element* el) {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConstVisitor);
+
+ friend class Element;
+};
+
// An XML DOM visitor that will record the package name for a namespace prefix.
class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
public:
using Visitor::Visit;
- Maybe<ExtractedPackage> TransformPackageAlias(
- const android::StringPiece& alias, const android::StringPiece& local_package) const override;
+ Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
protected:
PackageAwareVisitor() = default;
@@ -208,19 +243,19 @@ class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
namespace internal {
// Base class that overrides the default behaviour and does not descend into child nodes.
-class NodeCastBase : public Visitor {
+class NodeCastBase : public ConstVisitor {
public:
- void Visit(Element* el) override {
+ void Visit(const Element* el) override {
}
- void Visit(Text* el) override {
+ void Visit(const Text* el) override {
}
protected:
NodeCastBase() = default;
- void BeforeVisitElement(Element* el) override {
+ void BeforeVisitElement(const Element* el) override {
}
- void AfterVisitElement(Element* el) override {
+ void AfterVisitElement(const Element* el) override {
}
private:
@@ -234,9 +269,9 @@ class NodeCastImpl : public NodeCastBase {
NodeCastImpl() = default;
- T* value = nullptr;
+ const T* value = nullptr;
- void Visit(T* v) override {
+ void Visit(const T* v) override {
value = v;
}
@@ -247,12 +282,17 @@ class NodeCastImpl : public NodeCastBase {
} // namespace internal
template <typename T>
-T* NodeCast(Node* node) {
+const T* NodeCast(const Node* node) {
internal::NodeCastImpl<T> visitor;
node->Accept(&visitor);
return visitor.value;
}
+template <typename T>
+T* NodeCast(Node* node) {
+ return const_cast<T*>(NodeCast<T>(static_cast<const T*>(node)));
+}
+
} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index b501cfd4b8b9..4ba04430ced3 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -18,7 +18,7 @@
#include <string>
-#include "flatten/XmlFlattener.h"
+#include "format/binary/XmlFlattener.h"
#include "io/StringInputStream.h"
#include "test/Test.h"
@@ -117,19 +117,14 @@ class TestVisitor : public PackageAwareVisitor {
void Visit(Element* el) override {
if (el->name == "View1") {
- EXPECT_THAT(TransformPackageAlias("one", "local"),
- Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
} else if (el->name == "View2") {
- EXPECT_THAT(TransformPackageAlias("one", "local"),
- Eq(make_value(ExtractedPackage{"com.one", false})));
- EXPECT_THAT(TransformPackageAlias("two", "local"),
- Eq(make_value(ExtractedPackage{"com.two", false})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
} else if (el->name == "View3") {
- EXPECT_THAT(TransformPackageAlias("one", "local"),
- Eq(make_value(ExtractedPackage{"com.one", false})));
- EXPECT_THAT(TransformPackageAlias("two", "local"),
- Eq(make_value(ExtractedPackage{"com.two", false})));
- EXPECT_THAT(TransformPackageAlias("three", "local"),
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
+ EXPECT_THAT(TransformPackageAlias("three"),
Eq(make_value(ExtractedPackage{"com.three", false})));
}
}
@@ -143,7 +138,6 @@ TEST(XmlDomTest, PackageAwareXmlVisitor) {
</View2>
</View1>)");
- Debug::DumpXml(doc.get());
TestVisitor visitor;
doc->root->Accept(&visitor);
}
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 30bdc507303b..402e5a459f4e 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -141,17 +141,16 @@ const std::string& XmlPullParser::namespace_uri() const {
return event_queue_.front().data2;
}
-Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(
- const StringPiece& alias, const StringPiece& local_package) const {
+Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(const StringPiece& alias) const {
if (alias.empty()) {
- return ExtractedPackage{local_package.to_string(), false /* private */};
+ return ExtractedPackage{{}, false /*private*/};
}
const auto end_iter = package_aliases_.rend();
for (auto iter = package_aliases_.rbegin(); iter != end_iter; ++iter) {
if (alias == iter->prefix) {
if (iter->package.package.empty()) {
- return ExtractedPackage{local_package.to_string(), iter->package.private_namespace};
+ return ExtractedPackage{{}, iter->package.private_namespace};
}
return iter->package;
}
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index a00caa139061..63db66f0b2b7 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -119,8 +119,7 @@ class XmlPullParser : public IPackageDeclStack {
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
* 'package' will be set to 'defaultPackage'.
*/
- Maybe<ExtractedPackage> TransformPackageAlias(
- const android::StringPiece& alias, const android::StringPiece& local_package) const override;
+ Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
//
// Remaining methods are for retrieving information about attributes
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index fb8cee8b5634..c1186e83369c 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -62,19 +62,15 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(
return {};
}
-void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
- const StringPiece& local_package,
- Reference* in_ref) {
+void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref) {
if (in_ref->name) {
if (Maybe<ExtractedPackage> transformed_package =
- decl_stack->TransformPackageAlias(in_ref->name.value().package,
- local_package)) {
+ decl_stack->TransformPackageAlias(in_ref->name.value().package)) {
ExtractedPackage& extracted_package = transformed_package.value();
in_ref->name.value().package = std::move(extracted_package.package);
// If the reference was already private (with a * prefix) and the
- // namespace is public,
- // we keep the reference private.
+ // namespace is public, we keep the reference private.
in_ref->private_reference |= extracted_package.private_namespace;
}
}
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 866b6dcd7a88..4eb359a9eed4 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -35,7 +35,7 @@ constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
// Result of extracting a package name from a namespace URI declaration.
struct ExtractedPackage {
// The name of the package. This can be the empty string, which means that the package
- // should be assumed to be the package being compiled.
+ // should be assumed to be the same as the CallSite it was defined in.
std::string package;
// True if the package's private namespace was declared. This means that private resources
@@ -51,8 +51,8 @@ struct ExtractedPackage {
// http://schemas.android.com/apk/res/<package> or
// http://schemas.android.com/apk/prv/res/<package>
//
-// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
-// returns an empty package name.
+// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto, returns an empty
+// package name.
Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
// Returns an XML Android namespace for the given package of the form:
@@ -63,21 +63,20 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace
std::string BuildPackageNamespace(const android::StringPiece& package,
bool private_reference = false);
-// Interface representing a stack of XML namespace declarations. When looking up the package
-// for a namespace prefix, the stack is checked from top to bottom.
+// Interface representing a stack of XML namespace declarations. When looking up the package for a
+// namespace prefix, the stack is checked from top to bottom.
struct IPackageDeclStack {
virtual ~IPackageDeclStack() = default;
// Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
virtual Maybe<ExtractedPackage> TransformPackageAlias(
- const android::StringPiece& alias, const android::StringPiece& local_package) const = 0;
+ const android::StringPiece& alias) const = 0;
};
// Helper function for transforming the original Reference inRef to a fully qualified reference
// via the IPackageDeclStack. This will also mark the Reference as private if the namespace of the
// package declaration was private.
-void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
- const android::StringPiece& local_package, Reference* in_ref);
+void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref);
} // namespace xml
} // namespace aapt
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 77c1c24b17eb..2eab22e8ec14 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1127,6 +1127,80 @@ def verify_closable(clazz):
return
+def verify_member_name_not_kotlin_keyword(clazz):
+ """Prevent method names which are keywords in Kotlin."""
+
+ # https://kotlinlang.org/docs/reference/keyword-reference.html#hard-keywords
+ # This list does not include Java keywords as those are already impossible to use.
+ keywords = [
+ 'as',
+ 'fun',
+ 'in',
+ 'is',
+ 'object',
+ 'typealias',
+ 'val',
+ 'var',
+ 'when',
+ ]
+
+ for m in clazz.methods:
+ if m.name in keywords:
+ error(clazz, m, None, "Method name must not be a Kotlin keyword")
+ for f in clazz.fields:
+ if f.name in keywords:
+ error(clazz, f, None, "Field name must not be a Kotlin keyword")
+
+
+def verify_method_name_not_kotlin_operator(clazz):
+ """Warn about method names which become operators in Kotlin."""
+
+ binary = set()
+
+ def unique_binary_op(m, op):
+ if op in binary:
+ error(clazz, m, None, "Only one of '{0}' and '{0}Assign' methods should be present for Kotlin".format(op))
+ binary.add(op)
+
+ for m in clazz.methods:
+ if 'static' in m.split:
+ continue
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#unary-prefix-operators
+ if m.name in ['unaryPlus', 'unaryMinus', 'not'] and len(m.args) == 0:
+ warn(clazz, m, None, "Method can be invoked as a unary operator from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#increments-and-decrements
+ if m.name in ['inc', 'dec'] and len(m.args) == 0 and m.typ != 'void':
+ # This only applies if the return type is the same or a subtype of the enclosing class, but we have no
+ # practical way of checking that relationship here.
+ warn(clazz, m, None, "Method can be invoked as a pre/postfix inc/decrement operator from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#arithmetic
+ if m.name in ['plus', 'minus', 'times', 'div', 'rem', 'mod', 'rangeTo'] and len(m.args) == 1:
+ warn(clazz, m, None, "Method can be invoked as a binary operator from Kotlin")
+ unique_binary_op(m, m.name)
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#in
+ if m.name == 'contains' and len(m.args) == 1 and m.typ == 'boolean':
+ warn(clazz, m, None, "Method can be invoked as a 'in' operator from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#indexed
+ if (m.name == 'get' and len(m.args) > 0) or (m.name == 'set' and len(m.args) > 1):
+ warn(clazz, m, None, "Method can be invoked with an indexing operator from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#invoke
+ if m.name == 'invoke':
+ warn(clazz, m, None, "Method can be invoked with function call syntax from Kotlin")
+
+ # https://kotlinlang.org/docs/reference/operator-overloading.html#assignments
+ if m.name in ['plusAssign', 'minusAssign', 'timesAssign', 'divAssign', 'remAssign', 'modAssign'] \
+ and len(m.args) == 1 \
+ and m.typ == 'void':
+ warn(clazz, m, None, "Method can be invoked as a compound assignment operator from Kotlin")
+ unique_binary_op(m, m.name[:-6]) # Remove 'Assign' suffix
+
+
def examine_clazz(clazz):
"""Find all style issues in the given class."""
if clazz.pkg.name.startswith("java"): return
@@ -1178,6 +1252,8 @@ def examine_clazz(clazz):
verify_error(clazz)
verify_units(clazz)
verify_closable(clazz)
+ verify_member_name_not_kotlin_keyword(clazz)
+ verify_method_name_not_kotlin_operator(clazz)
def examine_stream(stream):
diff --git a/tools/bit/make.cpp b/tools/bit/make.cpp
index a8002417b916..b2ee99c2c74c 100644
--- a/tools/bit/make.cpp
+++ b/tools/bit/make.cpp
@@ -182,7 +182,7 @@ read_modules(const string& buildOut, const string& device, map<string,Module>* r
for (ssize_t i = module.classes.size() - 1; i >= 0; i--) {
string cl = module.classes[i];
if (!(cl == "JAVA_LIBRARIES" || cl == "EXECUTABLES" || cl == "SHARED_LIBRARIES"
- || cl == "APPS")) {
+ || cl == "APPS" || cl == "NATIVE_TESTS")) {
module.classes.erase(module.classes.begin() + i);
}
}
diff --git a/tools/incident_report/generic_message.h b/tools/incident_report/generic_message.h
index df3f7b22dfc7..7c4ad34e35c3 100644
--- a/tools/incident_report/generic_message.h
+++ b/tools/incident_report/generic_message.h
@@ -25,7 +25,7 @@ using namespace std;
/**
* Class to represent a protobuf Message, where we don't actually
* know what any of the fields are, just their type codes. In other
- * words, this loslessly stores a parsed protobuf object without
+ * words, this losslessly stores a parsed protobuf object without
* having the .proto file that generated it.
*/
class GenericMessage
diff --git a/tools/incident_report/main.cpp b/tools/incident_report/main.cpp
index 1d8809f6f603..cc252649da96 100644
--- a/tools/incident_report/main.cpp
+++ b/tools/incident_report/main.cpp
@@ -97,6 +97,8 @@ read_message(CodedInputStream* in, Descriptor const* descriptor, GenericMessage*
message->addInt64(fieldId, value64);
break;
} else {
+ fprintf(stderr, "bad VARINT: 0x%x (%d) at index %d of field %s\n",
+ tag, tag, in->CurrentPosition(), descriptor->name().c_str());
return false;
}
case WireFormatLite::WIRETYPE_FIXED64:
@@ -104,10 +106,14 @@ read_message(CodedInputStream* in, Descriptor const* descriptor, GenericMessage*
message->addInt64(fieldId, value64);
break;
} else {
+ fprintf(stderr, "bad VARINT: 0x%x (%d) at index %d of field %s\n",
+ tag, tag, in->CurrentPosition(), descriptor->name().c_str());
return false;
}
case WireFormatLite::WIRETYPE_LENGTH_DELIMITED:
if (!read_length_delimited(in, fieldId, descriptor, message)) {
+ fprintf(stderr, "bad LENGTH_DELIMITED: 0x%x (%d) at index %d of field %s\n",
+ tag, tag, in->CurrentPosition(), descriptor->name().c_str());
return false;
}
break;
@@ -116,11 +122,13 @@ read_message(CodedInputStream* in, Descriptor const* descriptor, GenericMessage*
message->addInt32(fieldId, value32);
break;
} else {
+ fprintf(stderr, "bad FIXED32: 0x%x (%d) at index %d of field %s\n",
+ tag, tag, in->CurrentPosition(), descriptor->name().c_str());
return false;
}
default:
- fprintf(stderr, "bad tag: 0x%x (%d) at index %d\n", tag, tag,
- in->CurrentPosition());
+ fprintf(stderr, "bad tag: 0x%x (%d) at index %d of field %s\n", tag, tag,
+ in->CurrentPosition(), descriptor->name().c_str());
return false;
}
}
@@ -130,7 +138,6 @@ read_message(CodedInputStream* in, Descriptor const* descriptor, GenericMessage*
static void
print_value(Out* out, FieldDescriptor const* field, GenericMessage::Node const& node)
{
- uint32_t val32;
FieldDescriptor::Type type = field->type();
switch (node.type) {
@@ -146,29 +153,32 @@ print_value(Out* out, FieldDescriptor const* field, GenericMessage::Node const&
out->printf("%f", *(float*)&node.value32);
break;
default:
- out->printf("(unexpected value %d (0x%x)", node.value32, node.value32);
+ out->printf("(unexpected type %d: value32 %d (0x%x)",
+ type, node.value32, node.value32);
break;
}
break;
case GenericMessage::TYPE_VALUE64:
switch (type) {
- case FieldDescriptor::TYPE_FIXED64:
- case FieldDescriptor::TYPE_SFIXED64:
case FieldDescriptor::TYPE_DOUBLE:
out->printf("%f", *(double*)&node.value64);
break;
+ // Int32s here were added with addInt64 from a WIRETYPE_VARINT,
+ // even if the definition is for a 32 bit int.
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_INT32:
- val32 = (uint32_t)node.value32;
- out->printf("%d", val32);
+ out->printf("%d", node.value64);
break;
case FieldDescriptor::TYPE_INT64:
- case FieldDescriptor::TYPE_UINT32:
- val32 = (uint32_t)node.value32;
- out->printf("%u", val32);
+ case FieldDescriptor::TYPE_SINT64:
+ case FieldDescriptor::TYPE_SFIXED64:
+ out->printf("%lld", node.value64);
break;
+ case FieldDescriptor::TYPE_UINT32:
case FieldDescriptor::TYPE_UINT64:
- case FieldDescriptor::TYPE_SINT64:
+ case FieldDescriptor::TYPE_FIXED64:
+ out->printf("%u", node.value64);
+ break;
case FieldDescriptor::TYPE_BOOL:
if (node.value64) {
out->printf("true");
@@ -177,8 +187,16 @@ print_value(Out* out, FieldDescriptor const* field, GenericMessage::Node const&
}
break;
case FieldDescriptor::TYPE_ENUM:
+ if (field->enum_type()->FindValueByNumber((int)node.value64) == NULL) {
+ out->printf("%lld", (int) node.value64);
+ } else {
+ out->printf("%s", field->enum_type()->FindValueByNumber((int)node.value64)
+ ->name().c_str());
+ }
+ break;
default:
- out->printf("(unexpected value %ld (0x%x))", node.value64, node.value64);
+ out->printf("(unexpected type %d: value64 %lld (0x%x))",
+ type, node.value64, node.value64);
break;
}
break;
@@ -213,22 +231,13 @@ print_message(Out* out, Descriptor const* descriptor, GenericMessage const* mess
out->printf("%s=", field->name().c_str());
if (repeated) {
if (it.first != it.second) {
- out->printf("[");
- if (type == FieldDescriptor::TYPE_MESSAGE
- || type == FieldDescriptor::TYPE_STRING
- || type == FieldDescriptor::TYPE_BYTES) {
- out->printf("\n");
- }
+ out->printf("[\n");
out->indent();
for (GenericMessage::const_iterator_pair it = message->find(fieldId);
it.first != it.second; it.first++) {
print_value(out, field, it.first->second);
- if (type == FieldDescriptor::TYPE_MESSAGE
- || type == FieldDescriptor::TYPE_STRING
- || type == FieldDescriptor::TYPE_BYTES) {
- out->printf("\n");
- }
+ out->printf("\n");
}
out->dedent();
@@ -297,7 +306,7 @@ static int
adb_incident_workaround(const char* adbSerial, const vector<string>& sections)
{
const int maxAllowedSize = 20 * 1024 * 1024; // 20MB
- uint8_t* buffer = (uint8_t*)malloc(maxAllowedSize);
+ unique_ptr<uint8_t[]> buffer(new uint8_t[maxAllowedSize]);
for (vector<string>::const_iterator it=sections.begin(); it!=sections.end(); it++) {
Descriptor const* descriptor = IncidentProto::descriptor();
@@ -324,7 +333,7 @@ adb_incident_workaround(const char* adbSerial, const vector<string>& sections)
}
id = field->number();
}
-
+
int pfd[2];
if (pipe(pfd) != 0) {
fprintf(stderr, "pipe failed: %s\n", strerror(errno));
@@ -363,7 +372,7 @@ adb_incident_workaround(const char* adbSerial, const vector<string>& sections)
size_t size = 0;
while (size < maxAllowedSize) {
- ssize_t amt = read(pfd[0], buffer + size, maxAllowedSize - size);
+ ssize_t amt = read(pfd[0], buffer.get() + size, maxAllowedSize - size);
if (amt == 0) {
break;
} else if (amt == -1) {
@@ -390,7 +399,7 @@ adb_incident_workaround(const char* adbSerial, const vector<string>& sections)
fprintf(stderr, "write error: %s\n", strerror(err));
return 1;
}
- err = write_all(STDOUT_FILENO, buffer, size);
+ err = write_all(STDOUT_FILENO, buffer.get(), size);
if (err != 0) {
fprintf(stderr, "write error: %s\n", strerror(err));
return 1;
@@ -401,7 +410,6 @@ adb_incident_workaround(const char* adbSerial, const vector<string>& sections)
}
}
- free(buffer);
return 0;
}
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 15f622cf9461..900690cf36d6 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -17,27 +17,34 @@
#include <frameworks/base/core/proto/android/os/incident.pb.h>
-
#include <map>
+#include <string>
+using namespace android;
using namespace android::os;
using namespace google::protobuf;
using namespace google::protobuf::io;
using namespace google::protobuf::internal;
using namespace std;
-int
-main(int, const char**)
-{
- map<string,FieldDescriptor const*> sections;
- int N;
+static inline void emptyline() {
+ printf("\n");
+}
+static void generateHead(const char* header) {
printf("// Auto generated file. Do not modify\n");
- printf("\n");
- printf("#include \"incident_sections.h\"\n");
- printf("\n");
+ emptyline();
+ printf("#include \"%s.h\"\n", header);
+ emptyline();
+}
- Descriptor const* descriptor = IncidentProto::descriptor();
+// ================================================================================
+static bool generateIncidentSectionsCpp(Descriptor const* descriptor)
+{
+ generateHead("incident_sections");
+
+ map<string,FieldDescriptor const*> sections;
+ int N;
N = descriptor->field_count();
for (int i=0; i<N; i++) {
const FieldDescriptor* field = descriptor->field(i);
@@ -63,5 +70,182 @@ main(int, const char**)
printf("const int INCIDENT_SECTION_COUNT = %d;\n", N);
- return 0;
+ return true;
+}
+
+// ================================================================================
+static void splitAndPrint(const string& args) {
+ size_t base = 0;
+ size_t found;
+ while (true) {
+ found = args.find_first_of(" ", base);
+ if (found != base) {
+ string arg = args.substr(base, found - base);
+ printf(" \"%s\",", arg.c_str());
+ }
+ if (found == args.npos) break;
+ base = found + 1;
+ }
+}
+
+static const std::string replaceAll(const string& field_name, const char oldC, const string& newS) {
+ if (field_name.find_first_of(oldC) == field_name.npos) return field_name.c_str();
+ size_t pos = 0, idx = 0;
+ char* res = new char[field_name.size() * newS.size() + 1]; // assign a larger buffer
+ while (pos != field_name.size()) {
+ char cur = field_name[pos++];
+ if (cur != oldC) {
+ res[idx++] = cur;
+ continue;
+ }
+
+ for (size_t i=0; i<newS.size(); i++) {
+ res[idx++] = newS[i];
+ }
+ }
+ res[idx] = '\0';
+ std::string result(res);
+ delete [] res;
+ return result;
+}
+
+static inline bool isDefaultDest(const FieldDescriptor* field) {
+ return field->options().GetExtension(privacy).dest() == PrivacyFlags::default_instance().dest();
+}
+
+// Returns true if the descriptor doesn't have any non default privacy flags set, including its submessages
+static bool generatePrivacyFlags(const Descriptor* descriptor, const char* alias, map<string, bool> &msgNames) {
+ bool hasDefaultFlags[descriptor->field_count()];
+ // iterate though its field and generate sub flags first
+ for (int i=0; i<descriptor->field_count(); i++) {
+ hasDefaultFlags[i] = true; // set default to true
+ const FieldDescriptor* field = descriptor->field(i);
+ const std::string field_name_str = replaceAll(field->full_name(), '.', "__");
+ const char* field_name = field_name_str.c_str();
+ // check if the same name is already defined
+ if (msgNames.find(field_name) != msgNames.end()) {
+ hasDefaultFlags[i] = msgNames[field_name];
+ continue;
+ };
+
+ PrivacyFlags p = field->options().GetExtension(privacy);
+
+ switch (field->type()) {
+ case FieldDescriptor::TYPE_MESSAGE:
+ if (generatePrivacyFlags(field->message_type(), field_name, msgNames) &&
+ isDefaultDest(field)) break;
+
+ printf("Privacy %s { %d, %d, %s_LIST, %d, NULL };\n", field_name, field->number(), field->type(), field_name, p.dest());
+ hasDefaultFlags[i] = false;
+ break;
+ case FieldDescriptor::TYPE_STRING:
+ if (isDefaultDest(field) && p.patterns_size() == 0) break;
+
+ printf("const char* %s_patterns[] = {\n", field_name);
+ for (int i=0; i<p.patterns_size(); i++) {
+ // the generated string need to escape backslash as well, need to dup it here
+ printf(" \"%s\",\n", replaceAll(p.patterns(i), '\\', "\\\\").c_str());
+ }
+ printf(" NULL };\n");
+ printf("Privacy %s { %d, %d, NULL, %d, %s_patterns };\n", field_name, field->number(), field->type(), p.dest(), field_name);
+ hasDefaultFlags[i] = false;
+ break;
+ default:
+ if (isDefaultDest(field)) break;
+ printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", field_name, field->number(), field->type(), p.dest());
+ hasDefaultFlags[i] = false;
+ }
+ // add the field name to message map, true means it has default flags
+ msgNames[field_name] = hasDefaultFlags[i];
+ }
+
+ bool allDefaults = true;
+ for (int i=0; i<descriptor->field_count(); i++) {
+ allDefaults &= hasDefaultFlags[i];
+ }
+ if (allDefaults) return true;
+
+ emptyline();
+
+ bool needConst = strcmp(alias, "PRIVACY_POLICY") == 0;
+ int policyCount = 0;
+
+ printf("%s Privacy* %s_LIST[] = {\n", needConst ? "const" : "", alias);
+ for (int i=0; i<descriptor->field_count(); i++) {
+ const FieldDescriptor* field = descriptor->field(i);
+ if (hasDefaultFlags[i]) continue;
+ printf(" &%s,\n", replaceAll(field->full_name(), '.', "__").c_str());
+ policyCount++;
+ }
+ if (needConst) {
+ printf("};\n\n");
+ printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount);
+ } else {
+ printf(" NULL };\n");
+ }
+ emptyline();
+ return false;
+}
+
+static bool generateSectionListCpp(Descriptor const* descriptor) {
+ generateHead("section_list");
+
+ // generates SECTION_LIST
+ printf("const Section* SECTION_LIST[] = {\n");
+ for (int i=0; i<descriptor->field_count(); i++) {
+ const FieldDescriptor* field = descriptor->field(i);
+
+ if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+ continue;
+ }
+ const SectionFlags s = field->options().GetExtension(section);
+ switch (s.type()) {
+ case SECTION_NONE:
+ continue;
+ case SECTION_FILE:
+ printf(" new FileSection(%d, \"%s\"),\n", field->number(), s.args().c_str());
+ break;
+ case SECTION_COMMAND:
+ printf(" new CommandSection(%d,", field->number());
+ splitAndPrint(s.args());
+ printf(" NULL),\n");
+ break;
+ case SECTION_DUMPSYS:
+ printf(" new DumpsysSection(%d,", field->number());
+ splitAndPrint(s.args());
+ printf(" NULL),\n");
+ break;
+ }
+ }
+ printf(" NULL };\n");
+ emptyline();
+
+ // generates PRIVACY_POLICY
+ map<string, bool> messageNames;
+ if (generatePrivacyFlags(descriptor, "PRIVACY_POLICY", messageNames)) {
+ // if no privacy options set at all, define an empty list
+ printf("const Privacy* PRIVACY_POLICY_LIST[] = {};\n");
+ printf("const int PRIVACY_POLICY_COUNT = 0;\n");
+ }
+
+ return true;
+}
+
+// ================================================================================
+int main(int argc, char const *argv[])
+{
+ if (argc != 2) return 1;
+ const char* module = argv[1];
+
+ Descriptor const* descriptor = IncidentProto::descriptor();
+
+ if (strcmp(module, "incident") == 0) {
+ return !generateIncidentSectionsCpp(descriptor);
+ }
+ if (strcmp(module, "incidentd") == 0 ) {
+ return !generateSectionListCpp(descriptor);
+ }
+
+ // return failure if not called by the whitelisted modules
+ return 1;
}
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 24068e9ffe92..756549c5e880 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -17,13 +17,31 @@
// ==========================================================
// Build the host executable: protoc-gen-javastream
// ==========================================================
-cc_binary_host {
- name: "protoc-gen-javastream",
+cc_defaults {
+ name: "protoc-gen-stream-defaults",
srcs: [
"Errors.cpp",
"string_utils.cpp",
- "main.cpp",
+ ],
+}
+
+
+cc_binary_host {
+ name: "protoc-gen-javastream",
+ srcs: [
+ "java/main.cpp",
+ ],
+
+ defaults: ["protoc-gen-stream-defaults"],
+ shared_libs: ["libprotoc"],
+}
+
+cc_binary_host {
+ name: "protoc-gen-cppstream",
+ srcs: [
+ "cpp/main.cpp",
],
+ defaults: ["protoc-gen-stream-defaults"],
shared_libs: ["libprotoc"],
}
diff --git a/tools/streaming_proto/Errors.cpp b/tools/streaming_proto/Errors.cpp
index 91c6b9245de0..0cd9037dcb55 100644
--- a/tools/streaming_proto/Errors.cpp
+++ b/tools/streaming_proto/Errors.cpp
@@ -3,7 +3,7 @@
#include <stdlib.h>
namespace android {
-namespace javastream_proto {
+namespace stream_proto {
Errors ERRORS;
@@ -82,6 +82,6 @@ Errors::HasErrors() const
return m_errors.size() > 0;
}
-} // namespace javastream_proto
+} // namespace stream_proto
} // namespace android
diff --git a/tools/streaming_proto/Errors.h b/tools/streaming_proto/Errors.h
index 109195a20b06..f14bbfd55b5f 100644
--- a/tools/streaming_proto/Errors.h
+++ b/tools/streaming_proto/Errors.h
@@ -4,7 +4,7 @@
#include <vector>
namespace android {
-namespace javastream_proto {
+namespace stream_proto {
using namespace std;
@@ -44,5 +44,5 @@ extern const string UNKNOWN_FILE;
extern const int UNKNOWN_LINE;
-} // namespace javastream_proto
+} // namespace stream_proto
} // namespace android
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
new file mode 100644
index 000000000000..d4e1b7aede92
--- /dev/null
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -0,0 +1,273 @@
+#include "Errors.h"
+#include "string_utils.h"
+
+#include "google/protobuf/compiler/plugin.pb.h"
+#include "google/protobuf/io/zero_copy_stream_impl.h"
+#include "google/protobuf/text_format.h"
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+using namespace android::stream_proto;
+using namespace google::protobuf;
+using namespace google::protobuf::compiler;
+using namespace google::protobuf::io;
+using namespace std;
+
+/**
+ * Position of the field type in a (long long) fieldId.
+ */
+const uint64_t FIELD_TYPE_SHIFT = 32;
+
+//
+// FieldId flags for whether the field is single, repeated or packed.
+// TODO: packed is not supported yet.
+//
+const uint64_t FIELD_COUNT_SHIFT = 40;
+const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_UNKNOWN = 0;
+const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
+
+// Indent
+const string INDENT = " ";
+
+/**
+ * See if this is the file for this request, and not one of the imported ones.
+ */
+static bool
+should_generate_for_file(const CodeGeneratorRequest& request, const string& file)
+{
+ const int N = request.file_to_generate_size();
+ for (int i=0; i<N; i++) {
+ if (request.file_to_generate(i) == file) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static string
+make_filename(const FileDescriptorProto& file_descriptor)
+{
+ return file_descriptor.name() + ".h";
+}
+
+static string
+get_proto_type(const FieldDescriptorProto& field)
+{
+ switch (field.type()) {
+ case FieldDescriptorProto::TYPE_DOUBLE:
+ return "double";
+ case FieldDescriptorProto::TYPE_FLOAT:
+ return "float";
+ case FieldDescriptorProto::TYPE_INT64:
+ return "int64";
+ case FieldDescriptorProto::TYPE_UINT64:
+ return "uint64";
+ case FieldDescriptorProto::TYPE_INT32:
+ return "int32";
+ case FieldDescriptorProto::TYPE_FIXED64:
+ return "fixed64";
+ case FieldDescriptorProto::TYPE_FIXED32:
+ return "fixed32";
+ case FieldDescriptorProto::TYPE_BOOL:
+ return "bool";
+ case FieldDescriptorProto::TYPE_STRING:
+ return "string";
+ case FieldDescriptorProto::TYPE_GROUP:
+ return "group<unsupported!>";
+ case FieldDescriptorProto::TYPE_MESSAGE:
+ return field.type_name();
+ case FieldDescriptorProto::TYPE_BYTES:
+ return "bytes";
+ case FieldDescriptorProto::TYPE_UINT32:
+ return "uint32";
+ case FieldDescriptorProto::TYPE_ENUM:
+ return field.type_name();
+ case FieldDescriptorProto::TYPE_SFIXED32:
+ return "sfixed32";
+ case FieldDescriptorProto::TYPE_SFIXED64:
+ return "sfixed64";
+ case FieldDescriptorProto::TYPE_SINT32:
+ return "sint32";
+ case FieldDescriptorProto::TYPE_SINT64:
+ return "sint64";
+ default:
+ // won't happen
+ return "void";
+ }
+}
+
+static void
+write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
+{
+ const int N = enu.value_size();
+ text << indent << "// enum " << enu.name() << endl;
+ for (int i=0; i<N; i++) {
+ const EnumValueDescriptorProto& value = enu.value(i);
+ text << indent << "const uint32_t "
+ << make_constant_name(value.name())
+ << " = " << value.number() << ";" << endl;
+ }
+ text << endl;
+}
+
+static uint64_t
+get_field_id(const FieldDescriptorProto& field)
+{
+ // Number
+ uint64_t result = (uint64_t)field.number();
+
+ // Type
+ result |= (uint64_t)field.type() << FIELD_TYPE_SHIFT;
+
+ // Count
+ if (field.options().packed()) {
+ result |= FIELD_COUNT_PACKED;
+ } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) {
+ result |= FIELD_COUNT_REPEATED;
+ } else {
+ result |= FIELD_COUNT_SINGLE;
+ }
+
+ return result;
+}
+
+static void
+write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
+{
+ string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL
+ ? "optional " : "";
+ string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED
+ ? "repeated " : "";
+ string proto_type = get_proto_type(field);
+ string packed_comment = field.options().packed()
+ ? " [packed=true]" : "";
+ text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
+ << field.name() << " = " << field.number() << packed_comment << ';' << endl;
+
+ text << indent << "const uint64_t " << make_constant_name(field.name()) << " = 0x";
+
+ ios::fmtflags fmt(text.flags());
+ text << setfill('0') << setw(16) << hex << get_field_id(field);
+ text.flags(fmt);
+
+ text << "LL;" << endl;
+
+ text << endl;
+}
+
+static void
+write_message(stringstream& text, const DescriptorProto& message, const string& indent)
+{
+ int N;
+ const string indented = indent + INDENT;
+
+ text << indent << "// message " << message.name() << endl;
+ text << indent << "class " << message.name() << " {" << endl;
+ text << indent << "public:" << endl;
+
+ // Enums
+ N = message.enum_type_size();
+ for (int i=0; i<N; i++) {
+ write_enum(text, message.enum_type(i), indented);
+ }
+
+ // Nested classes
+ N = message.nested_type_size();
+ for (int i=0; i<N; i++) {
+ write_message(text, message.nested_type(i), indented);
+ }
+
+ // Fields
+ N = message.field_size();
+ for (int i=0; i<N; i++) {
+ write_field(text, message.field(i), indented);
+ }
+
+ text << indent << "};" << endl;
+ text << endl;
+}
+
+static void
+write_cpp_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
+{
+ stringstream text;
+
+ text << "// Generated by protoc-gen-cppstream. DO NOT MODIFY." << endl;
+ text << "// source: " << file_descriptor.name() << endl << endl;
+
+ string header = "ANDROID_" + replace_string(file_descriptor.name(), '/', '_');
+ header = replace_string(header, '.', '_') + "_stream_h";
+ header = make_constant_name(header);
+
+ text << "#ifndef " << header << endl;
+ text << "#define " << header << endl;
+ text << endl;
+
+ vector<string> namespaces = split(file_descriptor.package(), '.');
+ for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) {
+ text << "namespace " << *it << " {" << endl;
+ }
+ text << endl;
+
+ size_t N;
+ N = file_descriptor.enum_type_size();
+ for (size_t i=0; i<N; i++) {
+ write_enum(text, file_descriptor.enum_type(i), "");
+ }
+
+ N = file_descriptor.message_type_size();
+ for (size_t i=0; i<N; i++) {
+ write_message(text, file_descriptor.message_type(i), "");
+ }
+
+ for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) {
+ text << "} // " << *it << endl;
+ }
+
+ text << endl;
+ text << "#endif // " << header << endl;
+
+ CodeGeneratorResponse::File* file_response = response->add_file();
+ file_response->set_name(make_filename(file_descriptor));
+ file_response->set_content(text.str());
+}
+
+int main(int argc, char const *argv[])
+{
+ (void)argc;
+ (void)argv;
+
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ CodeGeneratorRequest request;
+ CodeGeneratorResponse response;
+
+ // Read the request
+ request.ParseFromIstream(&cin);
+
+ // Build the files we need.
+ const int N = request.proto_file_size();
+ for (int i=0; i<N; i++) {
+ const FileDescriptorProto& file_descriptor = request.proto_file(i);
+ if (should_generate_for_file(request, file_descriptor.name())) {
+ write_cpp_file(&response, file_descriptor);
+ }
+ }
+
+ // If we had errors, don't write the response. Print the errors and exit.
+ if (ERRORS.HasErrors()) {
+ ERRORS.Print();
+ return 1;
+ }
+
+ // If we didn't have errors, write the response and exit happily.
+ response.SerializeToOstream(&cout);
+
+ /* code */
+ return 0;
+} \ No newline at end of file
diff --git a/tools/streaming_proto/main.cpp b/tools/streaming_proto/java/main.cpp
index 5b4ba04b2b58..b7d594bb9465 100644
--- a/tools/streaming_proto/main.cpp
+++ b/tools/streaming_proto/java/main.cpp
@@ -12,7 +12,7 @@
#include <sstream>
#include <map>
-using namespace android::javastream_proto;
+using namespace android::stream_proto;
using namespace google::protobuf;
using namespace google::protobuf::compiler;
using namespace google::protobuf::io;
diff --git a/tools/streaming_proto/string_utils.cpp b/tools/streaming_proto/string_utils.cpp
index cc738c4c108e..bd34ab7aa44d 100644
--- a/tools/streaming_proto/string_utils.cpp
+++ b/tools/streaming_proto/string_utils.cpp
@@ -3,7 +3,7 @@
#include <iostream>
namespace android {
-namespace javastream_proto {
+namespace stream_proto {
using namespace std;
@@ -89,7 +89,26 @@ replace_string(const string& str, const char replace, const char with)
return result;
}
-} // namespace javastream_proto
+vector<string>
+split(const string& str, const char delimiter)
+{
+ vector<string> result;
+ size_t base = 0, found = 0;
+ while (true) {
+ found = str.find_first_of(delimiter, base);
+ if (found != base) {
+ string part = str.substr(base, found - base);
+ if (!part.empty()) {
+ result.push_back(part);
+ }
+ }
+ if (found == str.npos) break;
+ base = found + 1;
+ }
+ return result;
+}
+
+} // namespace stream_proto
} // namespace android
diff --git a/tools/streaming_proto/string_utils.h b/tools/streaming_proto/string_utils.h
index ffe83ca99704..03284d16e1be 100644
--- a/tools/streaming_proto/string_utils.h
+++ b/tools/streaming_proto/string_utils.h
@@ -1,7 +1,8 @@
#include <string>
+#include <vector>
namespace android {
-namespace javastream_proto {
+namespace stream_proto {
using namespace std;
@@ -26,7 +27,11 @@ string file_base_name(const string& str);
*/
string replace_string(const string& str, const char replace, const char with);
+/**
+ * Split a string to parts by delimiter.
+ */
+vector<string> split(const string& str, const char delimiter);
-} // namespace javastream_proto
+} // namespace stream_proto
} // namespace android
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 5b45c551c930..bbfcba6272b2 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -20,13 +20,15 @@
#include <utils/PropertyMap.h>
#include <utils/String8.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace android;
-static const char* gProgName = "validatekeymaps";
+static const char* kProgName = "validatekeymaps";
+static bool gQuiet = false;
enum FileType {
FILETYPE_UNKNOWN,
@@ -36,15 +38,32 @@ enum FileType {
FILETYPE_INPUTDEVICECONFIGURATION,
};
+static void log(const char* fmt, ...) {
+ if (gQuiet) {
+ return;
+ }
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ va_end(args);
+}
+
+static void error(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
static void usage() {
- fprintf(stderr, "Keymap Validation Tool\n\n");
- fprintf(stderr, "Usage:\n");
- fprintf(stderr,
- " %s [*.kl] [*.kcm] [*.idc] [virtualkeys.*] [...]\n"
+ error("Keymap Validation Tool\n\n");
+ error("Usage:\n");
+ error(
+ " %s [-q] [*.kl] [*.kcm] [*.idc] [virtualkeys.*] [...]\n"
" Validates the specified key layouts, key character maps, \n"
- " input device configurations, or virtual key definitions.\n\n",
- gProgName);
+ " input device configurations, or virtual key definitions.\n\n"
+ " -q Quiet; do not write anything to standard out.\n",
+ kProgName);
}
static FileType getFileType(const char* filename) {
@@ -69,19 +88,19 @@ static FileType getFileType(const char* filename) {
}
static bool validateFile(const char* filename) {
- fprintf(stdout, "Validating file '%s'...\n", filename);
+ log("Validating file '%s'...\n", filename);
FileType fileType = getFileType(filename);
switch (fileType) {
case FILETYPE_UNKNOWN:
- fprintf(stderr, "Supported file types: *.kl, *.kcm, virtualkeys.*\n\n");
+ error("Supported file types: *.kl, *.kcm, virtualkeys.*\n\n");
return false;
case FILETYPE_KEYLAYOUT: {
sp<KeyLayoutMap> map;
status_t status = KeyLayoutMap::load(String8(filename), &map);
if (status) {
- fprintf(stderr, "Error %d parsing key layout file.\n\n", status);
+ error("Error %d parsing key layout file.\n\n", status);
return false;
}
break;
@@ -92,7 +111,7 @@ static bool validateFile(const char* filename) {
status_t status = KeyCharacterMap::load(String8(filename),
KeyCharacterMap::FORMAT_ANY, &map);
if (status) {
- fprintf(stderr, "Error %d parsing key character map file.\n\n", status);
+ error("Error %d parsing key character map file.\n\n", status);
return false;
}
break;
@@ -102,7 +121,7 @@ static bool validateFile(const char* filename) {
PropertyMap* map;
status_t status = PropertyMap::load(String8(filename), &map);
if (status) {
- fprintf(stderr, "Error %d parsing input device configuration file.\n\n", status);
+ error("Error %d parsing input device configuration file.\n\n", status);
return false;
}
delete map;
@@ -113,7 +132,7 @@ static bool validateFile(const char* filename) {
VirtualKeyMap* map;
status_t status = VirtualKeyMap::load(String8(filename), &map);
if (status) {
- fprintf(stderr, "Error %d parsing virtual key definition file.\n\n", status);
+ error("Error %d parsing virtual key definition file.\n\n", status);
return false;
}
delete map;
@@ -121,7 +140,7 @@ static bool validateFile(const char* filename) {
}
}
- fputs("No errors.\n\n", stdout);
+ log("No errors.\n\n");
return true;
}
@@ -133,15 +152,19 @@ int main(int argc, const char** argv) {
int result = 0;
for (int i = 1; i < argc; i++) {
+ if (i == 1 && !strcmp(argv[1], "-q")) {
+ gQuiet = true;
+ continue;
+ }
if (!validateFile(argv[i])) {
result = 1;
}
}
if (result) {
- fputs("Failed!\n", stderr);
+ error("Failed!\n");
} else {
- fputs("Success.\n", stdout);
+ log("Success.\n");
}
return result;
}