summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/AaptAssets.cpp2
-rw-r--r--tools/aapt/Android.bp11
-rw-r--r--tools/aapt/Command.cpp41
-rw-r--r--tools/aapt/ResourceTable.cpp3
-rw-r--r--tools/aapt/SdkConstants.h2
-rw-r--r--tools/aapt2/Android.bp12
-rw-r--r--tools/aapt2/Android.mk2
-rw-r--r--tools/aapt2/AppInfo.h13
-rw-r--r--tools/aapt2/Debug.cpp278
-rw-r--r--tools/aapt2/Debug.h1
-rw-r--r--tools/aapt2/Main.cpp2
-rw-r--r--tools/aapt2/NameMangler.h3
-rw-r--r--tools/aapt2/OWNERS2
-rw-r--r--tools/aapt2/Resource.cpp18
-rw-r--r--tools/aapt2/Resource.h157
-rw-r--r--tools/aapt2/ResourceParser.cpp167
-rw-r--r--tools/aapt2/ResourceParser.h8
-rw-r--r--tools/aapt2/ResourceParser_test.cpp34
-rw-r--r--tools/aapt2/ResourceTable.cpp34
-rw-r--r--tools/aapt2/ResourceTable.h24
-rw-r--r--tools/aapt2/ResourceTable_test.cpp8
-rw-r--r--tools/aapt2/ResourceUtils.cpp58
-rw-r--r--tools/aapt2/ResourceUtils.h19
-rw-r--r--tools/aapt2/ResourceUtils_test.cpp49
-rw-r--r--tools/aapt2/ResourceValues.cpp8
-rw-r--r--tools/aapt2/ResourceValues.h7
-rw-r--r--tools/aapt2/Resource_test.cpp103
-rw-r--r--tools/aapt2/SdkConstants.cpp10
-rw-r--r--tools/aapt2/SdkConstants.h4
-rw-r--r--tools/aapt2/Source.h7
-rw-r--r--tools/aapt2/cmd/Command.cpp2
-rw-r--r--tools/aapt2/cmd/Command.h24
-rw-r--r--tools/aapt2/cmd/Command_test.cpp2
-rw-r--r--tools/aapt2/cmd/Compile.cpp11
-rw-r--r--tools/aapt2/cmd/Compile.h19
-rw-r--r--tools/aapt2/cmd/Compile_test.cpp64
-rw-r--r--tools/aapt2/cmd/Convert.cpp3
-rw-r--r--tools/aapt2/cmd/Convert.h4
-rw-r--r--tools/aapt2/cmd/Diff.cpp4
-rw-r--r--tools/aapt2/cmd/Dump.cpp32
-rw-r--r--tools/aapt2/cmd/Dump.h20
-rw-r--r--tools/aapt2/cmd/Link.cpp43
-rw-r--r--tools/aapt2/cmd/Link.h30
-rw-r--r--tools/aapt2/cmd/Optimize.cpp8
-rw-r--r--tools/aapt2/cmd/Optimize.h14
-rw-r--r--tools/aapt2/cmd/Util.cpp37
-rw-r--r--tools/aapt2/cmd/Util.h8
-rw-r--r--tools/aapt2/compile/IdAssigner.cpp10
-rw-r--r--tools/aapt2/compile/IdAssigner_test.cpp14
-rw-r--r--tools/aapt2/compile/InlineXmlFormatParser.cpp4
-rw-r--r--tools/aapt2/compile/PseudolocaleGenerator.cpp2
-rw-r--r--tools/aapt2/compile/XmlIdCollector.cpp2
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.cpp66
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.h19
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.internal.h35
-rw-r--r--tools/aapt2/configuration/ConfigurationParser_test.cpp24
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp85
-rw-r--r--tools/aapt2/format/Archive.cpp2
-rw-r--r--tools/aapt2/format/binary/BinaryResourceParser.cpp12
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp6
-rw-r--r--tools/aapt2/format/binary/XmlFlattener.cpp2
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.cpp6
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize_test.cpp6
-rw-r--r--tools/aapt2/io/FileSystem.cpp5
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp30
-rw-r--r--tools/aapt2/java/JavaClassGenerator.h10
-rw-r--r--tools/aapt2/java/ManifestClassGenerator.cpp8
-rw-r--r--tools/aapt2/java/ProguardRules.cpp20
-rw-r--r--tools/aapt2/java/ProguardRules_test.cpp4
-rw-r--r--tools/aapt2/jni/ScopedUtfChars.h84
-rw-r--r--tools/aapt2/jni/aapt2_jni.cpp143
-rw-r--r--tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h37
-rw-r--r--tools/aapt2/link/AutoVersioner.cpp2
-rw-r--r--tools/aapt2/link/AutoVersioner_test.cpp16
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp55
-rw-r--r--tools/aapt2/link/ManifestFixer.h24
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp20
-rw-r--r--tools/aapt2/link/ReferenceLinker.h10
-rw-r--r--tools/aapt2/link/ReferenceLinker_test.cpp6
-rw-r--r--tools/aapt2/link/TableMerger.cpp6
-rw-r--r--tools/aapt2/link/TableMerger_test.cpp7
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp2
-rw-r--r--tools/aapt2/link/XmlReferenceLinker_test.cpp32
-rw-r--r--tools/aapt2/process/SymbolTable.cpp25
-rw-r--r--tools/aapt2/process/SymbolTable.h6
-rw-r--r--tools/aapt2/test/Builders.cpp6
-rw-r--r--tools/aapt2/test/Builders.h6
-rw-r--r--tools/aapt2/test/Common.cpp2
-rw-r--r--tools/aapt2/test/Common.h4
-rw-r--r--tools/aapt2/test/Context.h4
-rw-r--r--tools/aapt2/test/Fixture.cpp7
-rwxr-xr-xtools/aapt2/tools/finalize_res.py141
-rw-r--r--tools/aapt2/util/Files.cpp18
-rw-r--r--tools/aapt2/util/Files.h13
-rw-r--r--tools/aapt2/util/Maybe.h324
-rw-r--r--tools/aapt2/util/Maybe_test.cpp129
-rw-r--r--tools/aapt2/util/Util.cpp16
-rw-r--r--tools/aapt2/util/Util.h5
-rw-r--r--tools/aapt2/xml/XmlDom.cpp5
-rw-r--r--tools/aapt2/xml/XmlDom.h9
-rw-r--r--tools/aapt2/xml/XmlDom_test.cpp24
-rw-r--r--tools/aapt2/xml/XmlPullParser.cpp14
-rw-r--r--tools/aapt2/xml/XmlPullParser.h12
-rw-r--r--tools/aapt2/xml/XmlUtil.cpp6
-rw-r--r--tools/aapt2/xml/XmlUtil.h5
-rw-r--r--tools/aapt2/xml/XmlUtil_test.cpp2
-rwxr-xr-xtools/aosp/aosp_sha.sh19
-rwxr-xr-x[-rw-r--r--]tools/apilint/deprecated_at_birth.py19
-rw-r--r--tools/bit/command.cpp5
-rw-r--r--tools/bit/print.cpp1
-rw-r--r--tools/bit/util.h2
-rw-r--r--tools/codegen/OWNERS2
-rw-r--r--tools/codegen/src/com/android/codegen/ImportsProvider.kt21
-rw-r--r--tools/codegen/src/com/android/codegen/Utils.kt4
-rwxr-xr-xtools/finalize_res/finalize_res.py64
-rwxr-xr-xtools/fonts/fontchain_linter.py136
-rw-r--r--tools/lint/Android.bp46
-rw-r--r--tools/lint/OWNERS5
-rw-r--r--tools/lint/README.md84
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt50
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt577
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt83
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt169
-rw-r--r--tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt814
-rw-r--r--tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt217
-rw-r--r--tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt202
-rw-r--r--tools/locked_region_code_injection/Android.bp8
-rw-r--r--tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java6
-rw-r--r--tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java5
-rw-r--r--tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java4
-rw-r--r--tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt18
-rw-r--r--tools/sdkparcelables/Android.bp2
-rw-r--r--tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt2
-rw-r--r--tools/split-select/Android.bp1
-rw-r--r--tools/streaming_proto/Android.bp3
-rw-r--r--tools/streaming_proto/Errors.cpp1
-rw-r--r--tools/traceinjection/Android.bp49
-rw-r--r--tools/traceinjection/manifest.txt1
-rw-r--r--tools/traceinjection/src/com/android/traceinjection/Main.java121
-rw-r--r--tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java41
-rw-r--r--tools/traceinjection/src/com/android/traceinjection/TraceInjectionConfiguration.java52
-rw-r--r--tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java183
-rw-r--r--tools/traceinjection/test/com/android/traceinjection/InjectionTests.java246
-rw-r--r--tools/traceinjection/test/com/android/traceinjection/Trace.java22
-rw-r--r--tools/validatekeymaps/Android.bp2
-rw-r--r--tools/validatekeymaps/Main.cpp44
146 files changed, 4748 insertions, 1594 deletions
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 672731cc2939..899d26818548 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -319,12 +319,10 @@ int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int sta
// 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.
- bool hasRegion = false;
if (subtags[1].size() == 4) {
setScript(subtags[1]);
} else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
setRegion(subtags[1]);
- hasRegion = true;
} else {
fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n", part.string());
return -1;
diff --git a/tools/aapt/Android.bp b/tools/aapt/Android.bp
index a19d183d617b..cc10db9e1523 100644
--- a/tools/aapt/Android.bp
+++ b/tools/aapt/Android.bp
@@ -50,7 +50,6 @@ cc_defaults {
"libbase",
"libz",
],
- group_static_libs: true,
cflags: [
"-Wall",
@@ -62,16 +61,6 @@ cc_defaults {
enabled: true,
},
},
-
- // This tool is prebuilt if we're doing an app-only build.
- product_variables: {
- pdk: {
- enabled: false,
- },
- unbundled_build: {
- enabled: false,
- },
- },
}
// ==========================================================
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 812e2087f36b..fecc7b3cbf37 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -769,7 +769,7 @@ int doDump(Bundle* bundle)
config.country[1] = 'S';
config.orientation = ResTable_config::ORIENTATION_PORT;
config.density = ResTable_config::DENSITY_MEDIUM;
- config.sdkVersion = 10000; // Very high.
+ config.sdkVersion = SDK_CUR_DEVELOPMENT; // Very high.
config.screenWidthDp = 320;
config.screenHeightDp = 480;
config.smallestScreenWidthDp = 320;
@@ -1306,16 +1306,30 @@ int doDump(Bundle* bundle)
splitName.string()).string());
}
+ // For 'platformBuildVersionName', using both string and int type as a fallback
+ // since it may be the code name of Android or the API level.
String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
"platformBuildVersionName");
+ int32_t platformBuildVersionNameInt =
+ AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionName", 0,
+ NULL);
if (platformBuildVersionName != "") {
printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
+ } else if (platformBuildVersionNameInt > 0) {
+ printf(" platformBuildVersionName='%d'", platformBuildVersionNameInt);
}
+ // For 'platformBuildVersionCode', using both string and int type as a fallback
+ // since it may be the code name of Android or the API level.
String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
"platformBuildVersionCode");
+ int32_t platformBuildVersionCodeInt =
+ AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionCode", 0,
+ NULL);
if (platformBuildVersionCode != "") {
printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
+ } else if (platformBuildVersionCodeInt > 0) {
+ printf(" platformBuildVersionCode='%d'", platformBuildVersionCodeInt);
}
int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
@@ -1490,7 +1504,7 @@ int doDump(Bundle* bundle)
error.string());
goto bail;
}
- if (name == "Donut") targetSdk = 4;
+ if (name == "Donut") targetSdk = SDK_DONUT;
printf("sdkVersion:'%s'\n",
ResTable::normalizeForOutput(name.string()).string());
} else if (code != -1) {
@@ -1512,7 +1526,12 @@ int doDump(Bundle* bundle)
error.string());
goto bail;
}
- if (name == "Donut" && targetSdk < 4) targetSdk = 4;
+ if (name == "Donut" && targetSdk < SDK_DONUT) {
+ targetSdk = SDK_DONUT;
+ } else if (name != "" && targetSdk == 0) {
+ // Bump to current development version
+ targetSdk = SDK_CUR_DEVELOPMENT;
+ }
printf("targetSdkVersion:'%s'\n",
ResTable::normalizeForOutput(name.string()).string());
} else if (code != -1) {
@@ -2122,7 +2141,7 @@ int doDump(Bundle* bundle)
}
// Pre-1.6 implicitly granted permission compatibility logic
- if (targetSdk < 4) {
+ if (targetSdk < SDK_DONUT) {
if (!hasWriteExternalStoragePermission) {
printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
@@ -2149,7 +2168,7 @@ int doDump(Bundle* bundle)
}
// Pre-JellyBean call log permission compatibility.
- if (targetSdk < 16) {
+ if (targetSdk < SDK_JELLY_BEAN) {
if (!hasReadCallLogPermission && hasReadContactsPermission) {
printUsesPermission(String8("android.permission.READ_CALL_LOG"));
printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
@@ -2291,21 +2310,23 @@ int doDump(Bundle* bundle)
// the screen size support was introduced, so all default to
// enabled.
if (smallScreen > 0) {
- smallScreen = targetSdk >= 4 ? -1 : 0;
+ smallScreen = targetSdk >= SDK_DONUT ? -1 : 0;
}
if (normalScreen > 0) {
normalScreen = -1;
}
if (largeScreen > 0) {
- largeScreen = targetSdk >= 4 ? -1 : 0;
+ largeScreen = targetSdk >= SDK_DONUT ? -1 : 0;
}
if (xlargeScreen > 0) {
// Introduced in Gingerbread.
- xlargeScreen = targetSdk >= 9 ? -1 : 0;
+ xlargeScreen = targetSdk >= SDK_GINGERBREAD ? -1 : 0;
}
if (anyDensity > 0) {
- anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
- || compatibleWidthLimitDp > 0) ? -1 : 0;
+ anyDensity = (targetSdk >= SDK_DONUT || requiresSmallestWidthDp > 0 ||
+ compatibleWidthLimitDp > 0)
+ ? -1
+ : 0;
}
printf("supports-screens:");
if (smallScreen != 0) {
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 257e96b6e51a..b9de11b0026b 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2475,11 +2475,10 @@ void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
{
if (accessorCookie != NULL && fmt != NULL) {
AccessorCookie* ac = (AccessorCookie*)accessorCookie;
- int retval=0;
char buf[1024];
va_list ap;
va_start(ap, fmt);
- retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
buf, ac->attr.string(), ac->value.string());
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index daf91b032638..a146466402f6 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -48,6 +48,8 @@ enum {
SDK_R = 30,
SDK_S = 31,
SDK_S_V2 = 32,
+ SDK_TIRAMISU = 33,
+ SDK_CUR_DEVELOPMENT = 10000,
};
#endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 12dc156f75be..bfb32854a374 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -72,7 +72,6 @@ cc_defaults {
"libidmap2_policies",
],
stl: "libc++_static",
- group_static_libs: true,
}
// ==========================================================
@@ -166,21 +165,12 @@ cc_library_host_static {
],
proto: {
export_proto_headers: true,
+ type: "full",
},
defaults: ["aapt2_defaults"],
}
// ==========================================================
-// Build the host shared library: aapt2_jni
-// ==========================================================
-cc_library_host_shared {
- name: "libaapt2_jni",
- srcs: toolSources + ["jni/aapt2_jni.cpp"],
- static_libs: ["libaapt2"],
- defaults: ["aapt2_defaults"],
-}
-
-// ==========================================================
// Build the host tests: aapt2_tests
// ==========================================================
cc_test_host {
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index b165c6bed220..7b94e718fd0e 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -15,6 +15,8 @@ $(aapt2_results): .KATI_IMPLICIT_OUTPUTS := $(aapt2_results)-nocache
$(aapt2_results): $(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests
-$(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests --gtest_output=xml:$@ > /dev/null 2>&1
+$(call declare-0p-target,$(aapt2_results))
+
aapt2_results :=
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index d3ca357b0305..cabbe7ea7446 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -17,11 +17,10 @@
#ifndef AAPT_APP_INFO_H
#define AAPT_APP_INFO_H
+#include <optional>
#include <set>
#include <string>
-#include "util/Maybe.h"
-
namespace aapt {
// Information relevant to building an app, parsed from the app's AndroidManifest.xml.
@@ -30,19 +29,19 @@ struct AppInfo {
std::string package;
// The app's minimum SDK version, if it is defined.
- Maybe<int> min_sdk_version;
+ std::optional<int> min_sdk_version;
// The app's version code (the lower 32 bits of the long version code), if it is defined.
- Maybe<uint32_t> version_code;
+ std::optional<uint32_t> version_code;
// The app's version code major (the upper 32 bits of the long version code), if it is defined.
- Maybe<uint32_t> version_code_major;
+ std::optional<uint32_t> version_code_major;
// The app's revision code, if it is defined.
- Maybe<uint32_t> revision_code;
+ std::optional<uint32_t> revision_code;
// The app's split name, if it is a split.
- Maybe<std::string> split_name;
+ std::optional<std::string> split_name;
// The split names that this split depends on.
std::set<std::string> split_name_dependencies;
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index ef3a62f4efcc..f47d66ea5e87 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -16,6 +16,9 @@
#include "Debug.h"
+#include <androidfw/TypeWrappers.h>
+#include <format/binary/ResChunkPullParser.h>
+
#include <algorithm>
#include <map>
#include <memory>
@@ -23,17 +26,16 @@
#include <set>
#include <vector>
-#include "android-base/logging.h"
-#include "android-base/stringprintf.h"
-
#include "ResourceTable.h"
+#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "idmap2/Policies.h"
#include "text/Printer.h"
#include "util/Util.h"
-#include "idmap2/Policies.h"
-
using ::aapt::text::Printer;
using ::android::StringPiece;
using ::android::base::StringPrintf;
@@ -78,7 +80,7 @@ class ValueHeadlinePrinter : public ConstValueVisitor {
printer_->Print(parent_name.package);
printer_->Print(":");
}
- printer_->Print(to_string(parent_name.type));
+ printer_->Print(parent_name.type.to_string());
printer_->Print("/");
printer_->Print(parent_name.entry);
if (parent_ref.id) {
@@ -280,8 +282,7 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions&
printer->Indent();
for (const ResourceTableEntryView& entry : type.entries) {
printer->Print("resource ");
- printer->Print(ResourceId(package.id.value_or_default(0), type.id.value_or_default(0),
- entry.id.value_or_default(0))
+ printer->Print(ResourceId(package.id.value_or(0), type.id.value_or(0), entry.id.value_or(0))
.to_string());
printer->Print(" ");
@@ -362,7 +363,7 @@ void Debug::PrintStyleGraph(ResourceTable* table, const ResourceName& target_sty
continue;
}
- Maybe<ResourceTable::SearchResult> result = table->FindResource(style_name);
+ std::optional<ResourceTable::SearchResult> result = table->FindResource(style_name);
if (result) {
ResourceEntry* entry = result.value().entry;
for (const auto& value : entry->values) {
@@ -482,8 +483,7 @@ class XmlPrinter : public xml::ConstVisitor {
if (attr.compiled_attribute) {
printer_->Print("(");
- printer_->Print(
- attr.compiled_attribute.value().id.value_or_default(ResourceId(0)).to_string());
+ printer_->Print(attr.compiled_attribute.value().id.value_or(ResourceId(0)).to_string());
printer_->Print(")");
}
printer_->Print("=");
@@ -586,4 +586,260 @@ void Debug::DumpOverlayable(const ResourceTable& table, text::Printer* printer)
}
}
+namespace {
+
+using namespace android;
+
+class ChunkPrinter {
+ public:
+ ChunkPrinter(const void* data, size_t len, Printer* printer, IDiagnostics* diag)
+ : data_(data), data_len_(len), printer_(printer), diag_(diag) {
+ }
+
+ void PrintChunkHeader(const ResChunk_header* chunk) {
+ switch (util::DeviceToHost16(chunk->type)) {
+ case RES_STRING_POOL_TYPE:
+ printer_->Print("[RES_STRING_POOL_TYPE]");
+ break;
+ case RES_TABLE_LIBRARY_TYPE:
+ printer_->Print("[RES_TABLE_LIBRARY_TYPE]");
+ break;
+ case RES_TABLE_TYPE:
+ printer_->Print("[ResTable_header]");
+ break;
+ case RES_TABLE_PACKAGE_TYPE:
+ printer_->Print("[ResTable_package]");
+ break;
+ case RES_TABLE_TYPE_TYPE:
+ printer_->Print("[ResTable_type]");
+ break;
+ case RES_TABLE_TYPE_SPEC_TYPE:
+ printer_->Print("[RES_TABLE_TYPE_SPEC_TYPE]");
+ break;
+ default:
+ break;
+ }
+
+ printer_->Print(StringPrintf(" chunkSize: %u", util::DeviceToHost32(chunk->size)));
+ printer_->Print(StringPrintf(" headerSize: %u", util::DeviceToHost32(chunk->headerSize)));
+ }
+
+ bool PrintTable(const ResTable_header* chunk) {
+ printer_->Print(
+ StringPrintf(" Package count: %u\n", util::DeviceToHost32(chunk->packageCount)));
+
+ // Print the chunks contained within the table
+ printer_->Indent();
+ bool success = PrintChunk(
+ ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header)));
+ printer_->Undent();
+ return success;
+ }
+
+ void PrintResValue(const Res_value* value, const ConfigDescription& config,
+ const ResourceType* type) {
+ printer_->Print("[Res_value]");
+ printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(value->size)));
+ printer_->Print(StringPrintf(" dataType: 0x%02x", util::DeviceToHost32(value->dataType)));
+ printer_->Print(StringPrintf(" data: 0x%08x", util::DeviceToHost32(value->data)));
+
+ if (type) {
+ auto item =
+ ResourceUtils::ParseBinaryResValue(*type, config, value_pool_, *value, &out_pool_);
+ printer_->Print(" (");
+ item->PrettyPrint(printer_);
+ printer_->Print(")");
+ }
+
+ printer_->Print("\n");
+ }
+
+ bool PrintTableType(const ResTable_type* chunk) {
+ printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id)));
+ printer_->Print(StringPrintf(
+ " name: %s", util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1).c_str()));
+ printer_->Print(StringPrintf(" flags: 0x%02x", util::DeviceToHost32(chunk->flags)));
+ printer_->Print(StringPrintf(" entryCount: %u", util::DeviceToHost32(chunk->entryCount)));
+ printer_->Print(StringPrintf(" entryStart: %u", util::DeviceToHost32(chunk->entriesStart)));
+
+ ConfigDescription config;
+ config.copyFromDtoH(chunk->config);
+ printer_->Print(StringPrintf(" config: %s\n", config.to_string().c_str()));
+
+ const ResourceType* type =
+ ParseResourceType(util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1));
+
+ printer_->Indent();
+
+ TypeVariant tv(chunk);
+ for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
+ const ResTable_entry* entry = *it;
+ if (!entry) {
+ continue;
+ }
+
+ printer_->Print((entry->flags & ResTable_entry::FLAG_COMPLEX) ? "[ResTable_map_entry]"
+ : "[ResTable_entry]");
+ printer_->Print(StringPrintf(" id: 0x%04x", it.index()));
+ printer_->Print(StringPrintf(
+ " name: %s", util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)).c_str()));
+ printer_->Print(StringPrintf(" keyIndex: %u", util::DeviceToHost32(entry->key.index)));
+ printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(entry->size)));
+ printer_->Print(StringPrintf(" flags: 0x%04x", util::DeviceToHost32(entry->flags)));
+
+ printer_->Indent();
+
+ if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
+ auto map_entry = (const ResTable_map_entry*)entry;
+ printer_->Print(StringPrintf(" count: 0x%04x", util::DeviceToHost32(map_entry->count)));
+ printer_->Print(
+ StringPrintf(" parent: 0x%08x\n", util::DeviceToHost32(map_entry->parent.ident)));
+
+ // Print the name and value mappings
+ auto maps =
+ (const ResTable_map*)((const uint8_t*)entry + util::DeviceToHost32(entry->size));
+ for (size_t i = 0, count = util::DeviceToHost32(map_entry->count); i < count; i++) {
+ PrintResValue(&(maps[i].value), config, type);
+
+ printer_->Print(StringPrintf(
+ " name: %s name-id:%d\n",
+ util::GetString(key_pool_, util::DeviceToHost32(maps[i].name.ident)).c_str(),
+ util::DeviceToHost32(maps[i].name.ident)));
+ }
+ } else {
+ printer_->Print("\n");
+
+ // Print the value of the entry
+ auto value = (const Res_value*)((const uint8_t*)entry + util::DeviceToHost32(entry->size));
+ PrintResValue(value, config, type);
+ }
+
+ printer_->Undent();
+ }
+
+ printer_->Undent();
+ return true;
+ }
+
+ void PrintStringPool(const ResStringPool_header* chunk) {
+ // Initialize the string pools
+
+ ResStringPool* pool;
+ if (value_pool_.getError() == NO_INIT) {
+ pool = &value_pool_;
+ } else if (type_pool_.getError() == NO_INIT) {
+ pool = &type_pool_;
+ } else if (key_pool_.getError() == NO_INIT) {
+ pool = &key_pool_;
+ } else {
+ return;
+ }
+
+ pool->setTo(chunk,
+ util::DeviceToHost32((reinterpret_cast<const ResChunk_header*>(chunk))->size));
+
+ printer_->Print("\n");
+
+ for (size_t i = 0; i < pool->size(); i++) {
+ printer_->Print(StringPrintf("#%zd : %s\n", i, util::GetString(*pool, i).c_str()));
+ }
+ }
+
+ bool PrintPackage(const ResTable_package* chunk) {
+ printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id)));
+
+ size_t len = strnlen16((const char16_t*)chunk->name, std::size(chunk->name));
+ std::u16string package_name(len, u'\0');
+ package_name.resize(len);
+ for (size_t i = 0; i < len; i++) {
+ package_name[i] = util::DeviceToHost16(chunk->name[i]);
+ }
+
+ printer_->Print(StringPrintf("name: %s", String8(package_name.c_str()).c_str()));
+ printer_->Print(StringPrintf(" typeStrings: %u", util::DeviceToHost32(chunk->typeStrings)));
+ printer_->Print(
+ StringPrintf(" lastPublicType: %u", util::DeviceToHost32(chunk->lastPublicType)));
+ printer_->Print(StringPrintf(" keyStrings: %u", util::DeviceToHost32(chunk->keyStrings)));
+ printer_->Print(StringPrintf(" lastPublicKey: %u", util::DeviceToHost32(chunk->lastPublicKey)));
+ printer_->Print(StringPrintf(" typeIdOffset: %u\n", util::DeviceToHost32(chunk->typeIdOffset)));
+
+ // Print the chunks contained within the table
+ printer_->Indent();
+ bool success = PrintChunk(
+ ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header)));
+ printer_->Undent();
+ return success;
+ }
+
+ bool PrintChunk(ResChunkPullParser&& parser) {
+ while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
+ auto chunk = parser.chunk();
+ PrintChunkHeader(chunk);
+
+ switch (util::DeviceToHost16(chunk->type)) {
+ case RES_STRING_POOL_TYPE:
+ PrintStringPool(reinterpret_cast<const ResStringPool_header*>(chunk));
+ break;
+
+ case RES_TABLE_TYPE:
+ PrintTable(reinterpret_cast<const ResTable_header*>(chunk));
+ break;
+
+ case RES_TABLE_PACKAGE_TYPE:
+ type_pool_.uninit();
+ key_pool_.uninit();
+ PrintPackage(reinterpret_cast<const ResTable_package*>(chunk));
+ break;
+
+ case RES_TABLE_TYPE_TYPE:
+ PrintTableType(reinterpret_cast<const ResTable_type*>(chunk));
+ break;
+
+ default:
+ printer_->Print("\n");
+ break;
+ }
+ }
+
+ if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
+ diag_->Error(DiagMessage(source_) << "corrupt resource table: " << parser.error());
+ return false;
+ }
+
+ return true;
+ }
+
+ void Print() {
+ PrintChunk(ResChunkPullParser(data_, data_len_));
+ printer_->Print("[End]\n");
+ }
+
+ private:
+ const Source source_;
+ const void* data_;
+ const size_t data_len_;
+ Printer* printer_;
+ IDiagnostics* diag_;
+
+ // The standard value string pool for resource values.
+ ResStringPool value_pool_;
+
+ // The string pool that holds the names of the types defined
+ // in this table.
+ ResStringPool type_pool_;
+
+ // The string pool that holds the names of the entries defined
+ // in this table.
+ ResStringPool key_pool_;
+
+ StringPool out_pool_;
+};
+
+} // namespace
+
+void Debug::DumpChunks(const void* data, size_t len, Printer* printer, IDiagnostics* diag) {
+ ChunkPrinter chunk_printer(data, len, printer, diag);
+ chunk_printer.Print();
+}
+
} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 9443d606d7e5..4da92044cf2a 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -40,6 +40,7 @@ struct Debug {
static void DumpXml(const xml::XmlResource& doc, text::Printer* printer);
static void DumpResStringPool(const android::ResStringPool* pool, text::Printer* printer);
static void DumpOverlayable(const ResourceTable& table, text::Printer* printer);
+ static void DumpChunks(const void* data, size_t len, text::Printer* printer, IDiagnostics* diag);
};
} // namespace aapt
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 8a43bb4ede35..b249c6c128e1 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -147,7 +147,7 @@ class DaemonCommand : public Command {
private:
io::FileOutputStream* out_;
IDiagnostics* diagnostics_;
- Maybe<std::string> trace_folder_;
+ std::optional<std::string> trace_folder_;
};
} // namespace aapt
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index f1aad29a5c58..0b4905253d20 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -21,7 +21,6 @@
#include <string>
#include "Resource.h"
-#include "util/Maybe.h"
namespace aapt {
@@ -44,7 +43,7 @@ class NameMangler {
public:
explicit NameMangler(NameManglerPolicy policy) : policy_(policy) {}
- Maybe<ResourceName> MangleName(const ResourceName& name) {
+ std::optional<ResourceName> MangleName(const ResourceName& name) {
if (policy_.target_package_name == name.package ||
policy_.packages_to_mangle.count(name.package) == 0) {
return {};
diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS
index 69dfcc98340d..4f655e54a7c2 100644
--- a/tools/aapt2/OWNERS
+++ b/tools/aapt2/OWNERS
@@ -1,4 +1,4 @@
set noparent
toddke@google.com
-rtmitchell@google.com
+zyy@google.com
patb@google.com \ No newline at end of file
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 6364ccdd09e5..0bb330e26e6f 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -134,6 +134,24 @@ static const std::map<StringPiece, ResourceType> sResourceTypeMap{
{"xml", ResourceType::kXml},
};
+ResourceNamedTypeRef ResourceNamedTypeWithDefaultName(ResourceType t) {
+ return {to_string(t), t};
+}
+
+std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s) {
+ auto colon = std::find(s.begin(), s.end(), ':');
+ const ResourceType* parsedType;
+ if (colon != s.end() && colon != std::prev(s.end())) {
+ parsedType = ParseResourceType(s.substr(s.begin(), colon));
+ } else {
+ parsedType = ParseResourceType(s);
+ }
+ if (parsedType == nullptr) {
+ return std::nullopt;
+ }
+ return ResourceNamedTypeRef(s, *parsedType);
+}
+
const ResourceType* ParseResourceType(const StringPiece& str) {
auto iter = sResourceTypeMap.find(str);
if (iter == std::end(sResourceTypeMap)) {
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 307c21d9dc96..b41d8514230b 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -19,22 +19,21 @@
#include <iomanip>
#include <limits>
+#include <optional>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>
+#include "Source.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/StringPiece.h"
#include "utils/JenkinsHash.h"
-#include "Source.h"
-
namespace aapt {
/**
- * The various types of resource types available. Corresponds
- * to the 'type' in package:type/entry.
+ * The various types of resource types available.
*/
enum class ResourceType {
kAnim,
@@ -78,15 +77,63 @@ android::StringPiece to_string(ResourceType type);
const ResourceType* ParseResourceType(const android::StringPiece& str);
/**
+ * Pair of type name as in ResourceTable and actual resource type.
+ * Corresponds to the 'type' in package:type/entry.
+ *
+ * This is to support resource types with custom names inside resource tables.
+ */
+struct ResourceNamedType {
+ std::string name;
+ ResourceType type = ResourceType::kRaw;
+
+ ResourceNamedType() = default;
+ ResourceNamedType(const android::StringPiece& n, ResourceType t);
+
+ int compare(const ResourceNamedType& other) const;
+
+ const std::string& to_string() const;
+};
+
+/**
+ * Same as ResourceNamedType, but uses StringPieces instead.
+ * Use this if you need to avoid copying and know that
+ * the lifetime of this object is shorter than that
+ * of the original string.
+ */
+struct ResourceNamedTypeRef {
+ android::StringPiece name;
+ ResourceType type = ResourceType::kRaw;
+
+ ResourceNamedTypeRef() = default;
+ ResourceNamedTypeRef(const ResourceNamedTypeRef&) = default;
+ ResourceNamedTypeRef(ResourceNamedTypeRef&&) = default;
+ ResourceNamedTypeRef(const ResourceNamedType& rhs); // NOLINT(google-explicit-constructor)
+ ResourceNamedTypeRef(const android::StringPiece& n, ResourceType t);
+ ResourceNamedTypeRef& operator=(const ResourceNamedTypeRef& rhs) = default;
+ ResourceNamedTypeRef& operator=(ResourceNamedTypeRef&& rhs) = default;
+ ResourceNamedTypeRef& operator=(const ResourceNamedType& rhs);
+
+ ResourceNamedType ToResourceNamedType() const;
+
+ std::string to_string() const;
+};
+
+ResourceNamedTypeRef ResourceNamedTypeWithDefaultName(ResourceType t);
+
+std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s);
+
+/**
* A resource's name. This can uniquely identify
* a resource in the ResourceTable.
*/
struct ResourceName {
std::string package;
- ResourceType type = ResourceType::kRaw;
+ ResourceNamedType type;
std::string entry;
ResourceName() = default;
+ ResourceName(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+ const android::StringPiece& e);
ResourceName(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
int compare(const ResourceName& other) const;
@@ -103,13 +150,15 @@ struct ResourceName {
*/
struct ResourceNameRef {
android::StringPiece package;
- ResourceType type = ResourceType::kRaw;
+ ResourceNamedTypeRef type;
android::StringPiece entry;
ResourceNameRef() = default;
ResourceNameRef(const ResourceNameRef&) = default;
ResourceNameRef(ResourceNameRef&&) = default;
ResourceNameRef(const ResourceName& rhs); // NOLINT(google-explicit-constructor)
+ ResourceNameRef(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+ const android::StringPiece& e);
ResourceNameRef(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
@@ -295,17 +344,98 @@ inline ::std::ostream& operator<<(::std::ostream& out, const ResourceType& val)
}
//
+// ResourceNamedType implementation.
+//
+inline ResourceNamedType::ResourceNamedType(const android::StringPiece& n, ResourceType t)
+ : name(n.to_string()), type(t) {
+}
+
+inline int ResourceNamedType::compare(const ResourceNamedType& other) const {
+ int cmp = static_cast<int>(type) - static_cast<int>(other.type);
+ if (cmp != 0) return cmp;
+ cmp = name.compare(other.name);
+ return cmp;
+}
+
+inline const std::string& ResourceNamedType::to_string() const {
+ return name;
+}
+
+inline bool operator<(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+ return lhs.compare(rhs) < 0;
+}
+
+inline bool operator==(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+ return lhs.compare(rhs) == 0;
+}
+
+inline bool operator!=(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+ return lhs.compare(rhs) != 0;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNamedType& val) {
+ return out << val.to_string();
+}
+
+//
+// ResourceNamedTypeRef implementation.
+//
+inline ResourceNamedTypeRef::ResourceNamedTypeRef(const android::StringPiece& n, ResourceType t)
+ : name(n), type(t) {
+}
+
+inline ResourceNamedTypeRef::ResourceNamedTypeRef(const ResourceNamedType& rhs)
+ : name(rhs.name), type(rhs.type) {
+}
+
+inline ResourceNamedTypeRef& ResourceNamedTypeRef::operator=(const ResourceNamedType& rhs) {
+ name = rhs.name;
+ type = rhs.type;
+ return *this;
+}
+
+inline ResourceNamedType ResourceNamedTypeRef::ToResourceNamedType() const {
+ return ResourceNamedType(name, type);
+}
+
+inline std::string ResourceNamedTypeRef::to_string() const {
+ return name.to_string();
+}
+
+inline bool operator<(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+ return std::tie(lhs.type, lhs.name) < std::tie(rhs.type, rhs.name);
+}
+
+inline bool operator==(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+ return std::tie(lhs.type, lhs.name) == std::tie(rhs.type, rhs.name);
+}
+
+inline bool operator!=(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+ return std::tie(lhs.type, lhs.name) != std::tie(rhs.type, rhs.name);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNamedTypeRef& val) {
+ return out << val.name;
+}
+
+//
// ResourceName implementation.
//
+inline ResourceName::ResourceName(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+ const android::StringPiece& e)
+ : package(p.to_string()), type(t.ToResourceNamedType()), entry(e.to_string()) {
+}
+
inline ResourceName::ResourceName(const android::StringPiece& p, ResourceType t,
const android::StringPiece& e)
- : package(p.to_string()), type(t), entry(e.to_string()) {}
+ : ResourceName(p, ResourceNamedTypeWithDefaultName(t), e) {
+}
inline int ResourceName::compare(const ResourceName& other) const {
int cmp = package.compare(other.package);
if (cmp != 0) return cmp;
- cmp = static_cast<int>(type) - static_cast<int>(other.type);
+ cmp = type.compare(other.type);
if (cmp != 0) return cmp;
cmp = entry.compare(other.entry);
return cmp;
@@ -341,9 +471,16 @@ inline ::std::ostream& operator<<(::std::ostream& out, const ResourceName& name)
inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs)
: package(rhs.package), type(rhs.type), entry(rhs.entry) {}
+inline ResourceNameRef::ResourceNameRef(const android::StringPiece& p,
+ const ResourceNamedTypeRef& t,
+ const android::StringPiece& e)
+ : package(p), type(t), entry(e) {
+}
+
inline ResourceNameRef::ResourceNameRef(const android::StringPiece& p, ResourceType t,
const android::StringPiece& e)
- : package(p), type(t), entry(e) {}
+ : ResourceNameRef(p, ResourceNamedTypeWithDefaultName(t), e) {
+}
inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) {
package = rhs.package;
@@ -400,7 +537,7 @@ struct hash<aapt::ResourceName> {
size_t operator()(const aapt::ResourceName& name) const {
android::hash_t h = 0;
h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.package)));
- h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
+ h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.type.name)));
h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.entry)));
return static_cast<size_t>(h);
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index f1e2da9f41e2..8d35eeec2a93 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -20,7 +20,8 @@
#include <limits>
#include <sstream>
-#include "android-base/logging.h"
+#include <android-base/logging.h>
+#include <idmap2/Policies.h>
#include "ResourceTable.h"
#include "ResourceUtils.h"
@@ -28,12 +29,10 @@
#include "ValueVisitor.h"
#include "text/Utf8Iterator.h"
#include "util/ImmutableMap.h"
-#include "util/Maybe.h"
+
#include "util/Util.h"
#include "xml/XmlPullParser.h"
-#include "idmap2/Policies.h"
-
using ::aapt::ResourceUtils::StringBuilder;
using ::aapt::text::Utf8Iterator;
using ::android::ConfigDescription;
@@ -109,8 +108,8 @@ struct ParsedResource {
Visibility::Level visibility_level = Visibility::Level::kUndefined;
bool staged_api = false;
bool allow_new = false;
- Maybe<OverlayableItem> overlayable_item;
- Maybe<StagedId> staged_alias;
+ std::optional<OverlayableItem> overlayable_item;
+ std::optional<StagedId> staged_alias;
std::string comment;
std::unique_ptr<Value> value;
@@ -252,7 +251,7 @@ bool ResourceParser::FlattenXmlSubtree(
std::string current_text;
// The first occurrence of a <xliff:g> tag. Nested <xliff:g> tags are illegal.
- Maybe<size_t> untranslatable_start_depth;
+ std::optional<size_t> untranslatable_start_depth;
Node root;
std::vector<Node*> node_stack;
@@ -342,7 +341,7 @@ bool ResourceParser::FlattenXmlSubtree(
}
node_stack.pop_back();
- if (untranslatable_start_depth == make_value(depth)) {
+ if (untranslatable_start_depth == depth) {
// This is the end of an untranslatable section.
untranslatable_start_depth = {};
}
@@ -461,14 +460,14 @@ bool ResourceParser::ParseResources(xml::XmlPullParser* parser) {
ParsedResource parsed_resource;
parsed_resource.config = config_;
parsed_resource.source = source_.WithLine(parser->line_number());
- // NOLINTNEXTLINE(bugprone-use-after-move) move+reset comment
parsed_resource.comment = std::move(comment);
+ comment.clear();
if (options_.visibility) {
parsed_resource.visibility_level = options_.visibility.value();
}
// Extract the product name if it exists.
- if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
+ if (std::optional<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
parsed_resource.product = maybe_product.value().to_string();
}
@@ -560,7 +559,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
resource_format = android::ResTable_map::TYPE_ANY;
// Items have their type encoded in the type attribute.
- if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+ if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
resource_type = maybe_type.value().to_string();
} else {
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
@@ -568,7 +567,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
return false;
}
- if (Maybe<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
+ if (std::optional<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
// An explicit format for this resource was specified. The resource will
// retain its type in its name, but the accepted value for this type is
// overridden.
@@ -584,7 +583,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
can_be_item = false;
// Bags have their type encoded in the type attribute.
- if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+ if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
resource_type = maybe_type.value().to_string();
} else {
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
@@ -595,7 +594,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
// Get the name of the resource. This will be checked later, because not all
// XML elements require a name.
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (resource_type == "id") {
if (!maybe_name) {
@@ -605,7 +604,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
return false;
}
- out_resource->name.type = ResourceType::kId;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kId).ToResourceNamedType();
out_resource->name.entry = maybe_name.value().to_string();
// Ids either represent a unique resource id or reference another resource id
@@ -624,7 +624,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
// A null reference also means there is no inner element when ids are in the form:
// <id name="name"/>
out_resource->value = util::make_unique<Id>();
- } else if (!ref || ref->name.value().type != ResourceType::kId) {
+ } else if (!ref || ref->name.value().type.type != ResourceType::kId) {
// If an inner element exists, the inner element must be a reference to another resource id
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name()
@@ -641,7 +641,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
return false;
}
- out_resource->name.type = ResourceType::kMacro;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kMacro).ToResourceNamedType();
out_resource->name.entry = maybe_name.value().to_string();
return ParseMacro(parser, out_resource);
}
@@ -657,7 +658,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
return false;
}
- out_resource->name.type = item_iter->second.type;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(item_iter->second.type).ToResourceNamedType();
out_resource->name.entry = maybe_name.value().to_string();
// Only use the implied format of the type when there is no explicit format.
@@ -700,7 +702,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
if (can_be_item) {
// Try parsing the elementName (or type) as a resource. These shall only be
// resources like 'layout' or 'xml' and they can only be references.
- const ResourceType* parsed_type = ParseResourceType(resource_type);
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(resource_type);
if (parsed_type) {
if (!maybe_name) {
diag_->Error(DiagMessage(out_resource->source)
@@ -709,7 +711,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
return false;
}
- out_resource->name.type = *parsed_type;
+ out_resource->name.type = parsed_type->ToResourceNamedType();
out_resource->name.entry = maybe_name.value().to_string();
out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
if (!out_resource->value) {
@@ -835,10 +837,8 @@ std::unique_ptr<Item> ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub
bool ResourceParser::ParseString(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
bool formatted = true;
- if (Maybe<StringPiece> formatted_attr =
- xml::FindAttribute(parser, "formatted")) {
- Maybe<bool> maybe_formatted =
- ResourceUtils::ParseBool(formatted_attr.value());
+ if (std::optional<StringPiece> formatted_attr = xml::FindAttribute(parser, "formatted")) {
+ std::optional<bool> maybe_formatted = ResourceUtils::ParseBool(formatted_attr.value());
if (!maybe_formatted) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid value for 'formatted'. Must be a boolean");
@@ -848,8 +848,8 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser,
}
bool translatable = options_.translatable;
- if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
- Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
+ if (std::optional<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
+ std::optional<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
if (!maybe_translatable) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid value for 'translatable'. Must be a boolean");
@@ -929,14 +929,14 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out
<< "ignoring configuration '" << out_resource->config << "' for <public> tag");
}
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
diag_->Error(DiagMessage(out_resource->source)
<< "<public> must have a 'type' attribute");
return false;
}
- const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
if (!parsed_type) {
diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
<< maybe_type.value()
@@ -944,10 +944,10 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out
return false;
}
- out_resource->name.type = *parsed_type;
+ out_resource->name.type = parsed_type->ToResourceNamedType();
- if (Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
- Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
+ if (std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
+ std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
if (!maybe_id) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid resource ID '" << maybe_id_str.value() << "' in <public>");
@@ -956,7 +956,7 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out
out_resource->id = maybe_id.value();
}
- if (*parsed_type == ResourceType::kId) {
+ if (parsed_type->type == ResourceType::kId) {
// An ID marked as public is also the definition of an ID.
out_resource->value = util::make_unique<Id>();
}
@@ -974,28 +974,28 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou
<< "> tag");
}
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
diag->Error(DiagMessage(out_resource->source)
<< "<" << tag_name << "> must have a 'type' attribute");
return false;
}
- const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
if (!parsed_type) {
diag->Error(DiagMessage(out_resource->source)
<< "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
return false;
}
- Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
+ std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
if (!maybe_id_str) {
diag->Error(DiagMessage(out_resource->source)
<< "<" << tag_name << "> must have a 'first-id' attribute");
return false;
}
- Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
+ std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
if (!maybe_id) {
diag->Error(DiagMessage(out_resource->source)
<< "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">");
@@ -1038,11 +1038,19 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou
continue;
}
+ if (maybe_name.value().substr(0, std::strlen("removed_")) == "removed_") {
+ // Skip resources that have been removed from the framework, but leave a hole so that
+ // other staged resources don't shift and break apps previously compiled against them
+ next_id.id++;
+ continue;
+ }
+
ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
.name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()},
.source = item_source,
.comment = std::move(comment),
});
+ comment.clear();
// Execute group specific code.
func(entry_res, next_id);
@@ -1090,7 +1098,7 @@ bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource
bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name()
@@ -1098,7 +1106,7 @@ bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
return false;
}
- const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
if (!parsed_type) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid resource type '" << maybe_type.value() << "' in <"
@@ -1106,7 +1114,7 @@ bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
return false;
}
- out_resource->name.type = *parsed_type;
+ out_resource->name.type = parsed_type->ToResourceNamedType();
return true;
}
@@ -1137,7 +1145,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
<< "' for <overlayable> tag");
}
- Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
if (!overlayable_name) {
diag_->Error(DiagMessage(out_resource->source)
<< "<overlayable> tag must have a 'name' attribute");
@@ -1146,7 +1154,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
const std::string kActorUriScheme =
android::base::StringPrintf("%s://", Overlayable::kActorScheme);
- Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
+ std::optional<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
diag_->Error(DiagMessage(out_resource->source)
<< "specified <overlayable> tag 'actor' attribute must use the scheme '"
@@ -1194,7 +1202,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
}
// Items specify the name and type of resource that should be overlayable
- Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
if (!item_name) {
diag_->Error(DiagMessage(element_source)
<< "<item> within an <overlayable> must have a 'name' attribute");
@@ -1202,7 +1210,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
continue;
}
- Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
+ std::optional<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
if (!item_type) {
diag_->Error(DiagMessage(element_source)
<< "<item> within an <overlayable> must have a 'type' attribute");
@@ -1210,8 +1218,8 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
continue;
}
- const ResourceType* type = ParseResourceType(item_type.value());
- if (type == nullptr) {
+ std::optional<ResourceNamedTypeRef> type = ParseResourceNamedType(item_type.value());
+ if (!type) {
diag_->Error(DiagMessage(element_source)
<< "invalid resource type '" << item_type.value()
<< "' in <item> within an <overlayable>");
@@ -1225,7 +1233,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
overlayable_item.source = element_source;
ParsedResource child_resource{};
- child_resource.name.type = *type;
+ child_resource.name.type = type->ToResourceNamedType();
child_resource.name.entry = item_name.value().to_string();
child_resource.overlayable_item = overlayable_item;
out_resource->child_resources.push_back(std::move(child_resource));
@@ -1236,7 +1244,8 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
error = true;
break;
- } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+ } else if (std::optional<StringPiece> maybe_type =
+ xml::FindNonEmptyAttribute(parser, "type")) {
// Parse the polices separated by vertical bar characters to allow for specifying multiple
// policies. Items within the policy tag will have the specified policy.
for (const StringPiece& part : util::Tokenize(maybe_type.value(), '|')) {
@@ -1290,7 +1299,8 @@ bool ResourceParser::ParseAttr(xml::XmlPullParser* parser,
bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource, bool weak) {
- out_resource->name.type = ResourceType::kAttr;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kAttr).ToResourceNamedType();
// Attributes only end up in default configuration.
if (out_resource->config != ConfigDescription::DefaultConfig()) {
@@ -1302,7 +1312,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
uint32_t type_mask = 0;
- Maybe<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
+ std::optional<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
if (maybe_format) {
type_mask = ParseFormatAttribute(maybe_format.value());
if (type_mask == 0) {
@@ -1312,9 +1322,9 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
}
}
- Maybe<int32_t> maybe_min, maybe_max;
+ std::optional<int32_t> maybe_min, maybe_max;
- if (Maybe<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
+ if (std::optional<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
StringPiece min_str = util::TrimWhitespace(maybe_min_str.value());
if (!min_str.empty()) {
std::u16string min_str16 = util::Utf8ToUtf16(min_str);
@@ -1331,7 +1341,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
}
}
- if (Maybe<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
+ if (std::optional<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
StringPiece max_str = util::TrimWhitespace(maybe_max_str.value());
if (!max_str.empty()) {
std::u16string max_str16 = util::Utf8ToUtf16(max_str);
@@ -1398,8 +1408,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
type_mask |= android::ResTable_map::TYPE_FLAGS;
}
- if (Maybe<Attribute::Symbol> s =
- ParseEnumOrFlagItem(parser, element_name)) {
+ if (std::optional<Attribute::Symbol> s = ParseEnumOrFlagItem(parser, element_name)) {
Attribute::Symbol& symbol = s.value();
ParsedResource child_resource;
child_resource.name = symbol.symbol.name.value();
@@ -1443,24 +1452,24 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
type_mask ? type_mask : uint32_t{android::ResTable_map::TYPE_ANY});
attr->SetWeak(weak);
attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
- attr->min_int = maybe_min.value_or_default(std::numeric_limits<int32_t>::min());
- attr->max_int = maybe_max.value_or_default(std::numeric_limits<int32_t>::max());
+ attr->min_int = maybe_min.value_or(std::numeric_limits<int32_t>::min());
+ attr->max_int = maybe_max.value_or(std::numeric_limits<int32_t>::max());
out_resource->value = std::move(attr);
return true;
}
-Maybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
- xml::XmlPullParser* parser, const StringPiece& tag) {
+std::optional<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(xml::XmlPullParser* parser,
+ const StringPiece& tag) {
const Source source = source_.WithLine(parser->line_number());
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
diag_->Error(DiagMessage(source) << "no attribute 'name' found for tag <"
<< tag << ">");
return {};
}
- Maybe<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
+ std::optional<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
if (!maybe_value) {
diag_->Error(DiagMessage(source) << "no attribute 'value' found for tag <"
<< tag << ">");
@@ -1477,20 +1486,21 @@ Maybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
}
return Attribute::Symbol{
- Reference(ResourceNameRef({}, ResourceType::kId, maybe_name.value())),
+ Reference(ResourceNameRef({}, ResourceNamedTypeWithDefaultName(ResourceType::kId),
+ maybe_name.value())),
val.data, val.dataType};
}
bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
const Source source = source_.WithLine(parser->line_number());
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
diag_->Error(DiagMessage(source) << "<item> must have a 'name' attribute");
return false;
}
- Maybe<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+ std::optional<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
if (!maybe_key) {
diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'");
return false;
@@ -1511,11 +1521,11 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
ParsedResource* out_resource) {
- out_resource->name.type = type;
+ out_resource->name.type = ResourceNamedTypeWithDefaultName(type).ToResourceNamedType();
std::unique_ptr<Style> style = util::make_unique<Style>();
- Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
+ std::optional<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 (!maybe_parent.value().empty()) {
@@ -1537,7 +1547,8 @@ 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(
+ {}, ResourceNamedTypeWithDefaultName(ResourceType::kStyle), style_name.substr(0, pos)));
}
}
@@ -1571,7 +1582,7 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
bool ResourceParser::ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
uint32_t resource_format = android::ResTable_map::TYPE_ANY;
- if (Maybe<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
+ if (std::optional<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
resource_format = ParseFormatTypeNoEnumsOrFlags(format_attr.value());
if (resource_format == 0u) {
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
@@ -1593,13 +1604,14 @@ bool ResourceParser::ParseStringArray(xml::XmlPullParser* parser, ParsedResource
bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource,
const uint32_t typeMask) {
- out_resource->name.type = ResourceType::kArray;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kArray).ToResourceNamedType();
std::unique_ptr<Array> array = util::make_unique<Array>();
bool translatable = options_.translatable;
- if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
- Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
+ if (std::optional<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
+ std::optional<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
if (!maybe_translatable) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid value for 'translatable'. Must be a boolean");
@@ -1648,7 +1660,8 @@ bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
- out_resource->name.type = ResourceType::kPlurals;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kPlurals).ToResourceNamedType();
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
@@ -1664,8 +1677,7 @@ bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
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_quantity =
- xml::FindNonEmptyAttribute(parser, "quantity");
+ std::optional<StringPiece> maybe_quantity = xml::FindNonEmptyAttribute(parser, "quantity");
if (!maybe_quantity) {
diag_->Error(DiagMessage(item_source)
<< "<item> in <plurals> requires attribute "
@@ -1730,7 +1742,8 @@ bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
- out_resource->name.type = ResourceType::kStyleable;
+ out_resource->name.type =
+ ResourceNamedTypeWithDefaultName(ResourceType::kStyleable).ToResourceNamedType();
if (!options_.preserve_visibility_of_styleables) {
// This was added in change Idd21b5de4d20be06c6f8c8eb5a22ccd68afc4927 to mimic aapt1, but no one
@@ -1767,7 +1780,7 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
const std::string& element_namespace = parser->element_namespace();
const std::string& element_name = parser->element_name();
if (element_namespace.empty() && element_name == "attr") {
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
diag_->Error(DiagMessage(item_source) << "<attr> tag must have a 'name' attribute");
error = true;
@@ -1777,7 +1790,7 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
// If this is a declaration, the package name may be in the name. Separate
// these out.
// Eg. <attr name="android:text" />
- Maybe<Reference> maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+ std::optional<Reference> maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
if (!maybe_ref) {
diag_->Error(DiagMessage(item_source) << "<attr> tag has invalid name '"
<< maybe_name.value() << "'");
@@ -1792,8 +1805,8 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
ParsedResource child_resource;
child_resource.name = child_ref.name.value();
child_resource.source = item_source;
- // NOLINTNEXTLINE(bugprone-use-after-move) move+reset comment
child_resource.comment = std::move(comment);
+ comment.clear();
if (options_.visibility) {
child_resource.visibility_level = options_.visibility.value();
}
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 261499781638..548f5f9531fd 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -18,6 +18,7 @@
#define AAPT_RESOURCE_PARSER_H
#include <memory>
+#include <optional>
#include "android-base/macros.h"
#include "androidfw/ConfigDescription.h"
@@ -27,7 +28,6 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "StringPool.h"
-#include "util/Maybe.h"
#include "xml/XmlPullParser.h"
namespace aapt {
@@ -54,7 +54,7 @@ struct ResourceParserOptions {
// If visibility was forced, we need to use it when creating a new resource and also error if we
// try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
- Maybe<Visibility::Level> visibility;
+ std::optional<Visibility::Level> visibility;
};
struct FlattenedXmlSubTree {
@@ -122,8 +122,8 @@ class ResourceParser {
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);
- Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
- const android::StringPiece& tag);
+ std::optional<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
+ const android::StringPiece& tag);
bool ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 279ebcba2f71..556ffa221db5 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -567,12 +567,12 @@ TEST_F(ResourceParserTest, ParseStyle) {
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_THAT(style, NotNull());
ASSERT_TRUE(style->parent);
- EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/fu"))));
+ EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("style/fu")));
ASSERT_THAT(style->entries, SizeIs(3));
- EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
- EXPECT_THAT(style->entries[1].key.name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
- EXPECT_THAT(style->entries[2].key.name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
+ EXPECT_THAT(style->entries[0].key.name, Eq(test::ParseNameOrDie("attr/bar")));
+ EXPECT_THAT(style->entries[1].key.name, Eq(test::ParseNameOrDie("attr/bat")));
+ EXPECT_THAT(style->entries[2].key.name, Eq(test::ParseNameOrDie("attr/baz")));
}
TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
@@ -581,7 +581,7 @@ TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_THAT(style, NotNull());
ASSERT_TRUE(style->parent);
- EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("com.app:style/Theme"))));
+ EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("com.app:style/Theme")));
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
@@ -594,7 +594,7 @@ TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
ASSERT_THAT(style, NotNull());
ASSERT_TRUE(style->parent);
ASSERT_TRUE(style->parent.value().name);
- EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("android:style/Theme"))));
+ EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("android:style/Theme")));
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
@@ -607,7 +607,7 @@ TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_THAT(style, NotNull());
ASSERT_THAT(style->entries, SizeIs(1));
- EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("android:attr/bar"))));
+ EXPECT_THAT(style->entries[0].key.name, Eq(test::ParseNameOrDie("android:attr/bar")));
}
TEST_F(ResourceParserTest, ParseStyleWithRawStringItem) {
@@ -634,7 +634,7 @@ TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
ASSERT_THAT(style, NotNull());
ASSERT_TRUE(style->parent);
- EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/foo"))));
+ EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("style/foo")));
EXPECT_TRUE(style->parent_inferred);
}
@@ -672,7 +672,7 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
</declare-styleable>)";
ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result =
+ std::optional<ResourceTable::SearchResult> result =
table_.FindResource(test::ParseNameOrDie("styleable/foo"));
ASSERT_TRUE(result);
EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
@@ -695,9 +695,9 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
ASSERT_THAT(styleable, NotNull());
ASSERT_THAT(styleable->entries, SizeIs(3));
- EXPECT_THAT(styleable->entries[0].name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
- EXPECT_THAT(styleable->entries[1].name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
- EXPECT_THAT(styleable->entries[2].name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
+ EXPECT_THAT(styleable->entries[0].name, Eq(test::ParseNameOrDie("attr/bar")));
+ EXPECT_THAT(styleable->entries[1].name, Eq(test::ParseNameOrDie("attr/bat")));
+ EXPECT_THAT(styleable->entries[2].name, Eq(test::ParseNameOrDie("attr/baz")));
}
TEST_F(ResourceParserTest, ParseDeclareStyleablePreservingVisibility) {
@@ -913,7 +913,8 @@ TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
</public-group>)";
ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+ std::optional<ResourceTable::SearchResult> result =
+ table_.FindResource(test::ParseNameOrDie("attr/foo"));
ASSERT_TRUE(result);
ASSERT_TRUE(result.value().entry->id);
EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01010040)));
@@ -932,7 +933,8 @@ TEST_F(ResourceParserTest, StagingPublicGroup) {
</staging-public-group>)";
ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+ std::optional<ResourceTable::SearchResult> result =
+ table_.FindResource(test::ParseNameOrDie("attr/foo"));
ASSERT_TRUE(result);
ASSERT_TRUE(result.value().entry->id);
@@ -959,7 +961,7 @@ TEST_F(ResourceParserTest, StrongestSymbolVisibilityWins) {
<java-symbol type="string" name="foo" />)";
ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result =
+ std::optional<ResourceTable::SearchResult> result =
table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(result);
@@ -977,7 +979,7 @@ TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
ASSERT_TRUE(TestParse(R"(<add-resource name="bar" type="string" />)"));
- Maybe<ResourceTable::SearchResult> result =
+ std::optional<ResourceTable::SearchResult> result =
table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(result);
const ResourceEntry* entry = result.value().entry;
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 8ab1493c6ab3..98cce268e213 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -18,6 +18,7 @@
#include <algorithm>
#include <memory>
+#include <optional>
#include <tuple>
#include "android-base/logging.h"
@@ -68,7 +69,7 @@ struct NameEqualRange {
template <typename T, typename U>
bool less_than_struct_with_name_and_id(const T& lhs,
- const std::pair<std::string_view, Maybe<U>>& rhs) {
+ const std::pair<std::string_view, std::optional<U>>& rhs) {
if (lhs.id != rhs.second) {
return lhs.id < rhs.second;
}
@@ -341,20 +342,20 @@ struct EntryViewComparer {
void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePackage* package,
const ResourceTableType* type, const std::string& entry_name,
- const Maybe<ResourceId>& id, const Visibility& visibility,
- const Maybe<AllowNew>& allow_new,
- const Maybe<OverlayableItem>& overlayable_item,
- const Maybe<StagedId>& staged_id,
+ const std::optional<ResourceId>& id, const Visibility& visibility,
+ const std::optional<AllowNew>& allow_new,
+ const std::optional<OverlayableItem>& overlayable_item,
+ const std::optional<StagedId>& staged_id,
const std::vector<std::unique_ptr<ResourceConfigValue>>& values) {
SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
SortedVectorInserter<ResourceTableEntryView, EntryViewComparer> entry_inserter;
ResourceTablePackageView new_package{package->name,
- id ? id.value().package_id() : Maybe<uint8_t>{}};
+ id ? id.value().package_id() : std::optional<uint8_t>{}};
auto view_package = package_inserter.Insert(table.packages, std::move(new_package));
- ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : Maybe<uint8_t>{}};
+ ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : std::optional<uint8_t>{}};
auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
if (visibility.level == Visibility::Level::kPublic) {
@@ -363,7 +364,7 @@ void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePacka
}
ResourceTableEntryView new_entry{.name = entry_name,
- .id = id ? id.value().entry_id() : Maybe<uint16_t>{},
+ .id = id ? id.value().entry_id() : std::optional<uint16_t>{},
.visibility = visibility,
.allow_new = allow_new,
.overlayable_item = overlayable_item,
@@ -435,11 +436,11 @@ ResourceTableView ResourceTable::GetPartitionedView(const ResourceTableViewOptio
const size_t index = type_index_iter->second;
if (new_packages.size() == index) {
new_packages.emplace_back(ResourceTablePackageView{package.name, package.id});
- type_new_package_index[type.type] = index + 1;
}
// Move the type into a new package
auto& other_package = new_packages[index];
+ type_new_package_index[type.type] = index + 1;
type_inserter.Insert(other_package.types, std::move(type));
type_it = package.types.erase(type_it);
}
@@ -472,7 +473,7 @@ bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) {
}
auto package = FindOrCreatePackage(res.name.package);
- auto type = package->FindOrCreateType(res.name.type);
+ auto type = package->FindOrCreateType(res.name.type.type);
auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), res.name.entry,
NameEqualRange<ResourceEntry>{});
const size_t entry_count = std::distance(entry_it.first, entry_it.second);
@@ -585,13 +586,14 @@ bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) {
return true;
}
-Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
+std::optional<ResourceTable::SearchResult> ResourceTable::FindResource(
+ const ResourceNameRef& name) const {
ResourceTablePackage* package = FindPackage(name.package);
if (package == nullptr) {
return {};
}
- ResourceTableType* type = package->FindType(name.type);
+ ResourceTableType* type = package->FindType(name.type.type);
if (type == nullptr) {
return {};
}
@@ -603,14 +605,14 @@ Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNam
return SearchResult{package, type, entry};
}
-Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name,
- ResourceId id) const {
+std::optional<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name,
+ ResourceId id) const {
ResourceTablePackage* package = FindPackage(name.package);
if (package == nullptr) {
return {};
}
- ResourceTableType* type = package->FindType(name.type);
+ ResourceTableType* type = package->FindType(name.type.type);
if (type == nullptr) {
return {};
}
@@ -631,7 +633,7 @@ bool ResourceTable::RemoveResource(const ResourceNameRef& name, ResourceId id) c
return {};
}
- ResourceTableType* type = package->FindType(name.type);
+ ResourceTableType* type = package->FindType(name.type.type);
if (type == nullptr) {
return {};
}
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index bae1d827a841..2e17659b0679 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -120,18 +120,18 @@ class ResourceEntry {
const std::string name;
// The entry ID for this resource (the EEEE in 0xPPTTEEEE).
- Maybe<ResourceId> id;
+ std::optional<ResourceId> id;
// Whether this resource is public (and must maintain the same entry ID across builds).
Visibility visibility;
- Maybe<AllowNew> allow_new;
+ std::optional<AllowNew> allow_new;
// The declarations of this resource as overlayable for RROs
- Maybe<OverlayableItem> overlayable_item;
+ std::optional<OverlayableItem> overlayable_item;
// The staged resource id for a finalized resource.
- Maybe<StagedId> staged_id;
+ std::optional<StagedId> staged_id;
// The resource's values for each configuration.
std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -205,11 +205,11 @@ class ResourceTablePackage {
struct ResourceTableEntryView {
std::string name;
- Maybe<uint16_t> id;
+ std::optional<uint16_t> id;
Visibility visibility;
- Maybe<AllowNew> allow_new;
- Maybe<OverlayableItem> overlayable_item;
- Maybe<StagedId> staged_id;
+ std::optional<AllowNew> allow_new;
+ std::optional<OverlayableItem> overlayable_item;
+ std::optional<StagedId> staged_id;
std::vector<const ResourceConfigValue*> values;
const ResourceConfigValue* FindValue(const android::ConfigDescription& config,
@@ -218,7 +218,7 @@ struct ResourceTableEntryView {
struct ResourceTableTypeView {
ResourceType type;
- Maybe<uint8_t> id;
+ std::optional<uint8_t> id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
// Entries sorted in ascending entry id order. If ids have not been assigned, the entries are
@@ -228,7 +228,7 @@ struct ResourceTableTypeView {
struct ResourceTablePackageView {
std::string name;
- Maybe<uint8_t> id;
+ std::optional<uint8_t> id;
// Types sorted in ascending type id order. If ids have not been assigned, the types are sorted by
// their declaration order in the ResourceType enum.
std::vector<ResourceTableTypeView> types;
@@ -309,8 +309,8 @@ class ResourceTable {
ResourceEntry* entry;
};
- Maybe<SearchResult> FindResource(const ResourceNameRef& name) const;
- Maybe<SearchResult> FindResource(const ResourceNameRef& name, ResourceId id) const;
+ std::optional<SearchResult> FindResource(const ResourceNameRef& name) const;
+ std::optional<SearchResult> FindResource(const ResourceNameRef& name, ResourceId id) const;
bool RemoveResource(const ResourceNameRef& name, ResourceId id) const;
// Returns the package struct with the given name, or nullptr if such a package does not
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 38391c99f55a..de73d2c203e4 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -162,7 +162,7 @@ TEST(ResourceTableTest, ProductVaryingValues) {
EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "tablet"), NotNull());
EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "phone"), NotNull());
- Maybe<ResourceTable::SearchResult> sr =
+ std::optional<ResourceTable::SearchResult> sr =
table.FindResource(test::ParseNameOrDie("android:string/foo"));
ASSERT_TRUE(sr);
std::vector<ResourceConfigValue*> values =
@@ -187,7 +187,7 @@ static ::testing::AssertionResult VisibilityOfResource(const ResourceTable& tabl
const ResourceNameRef& name,
Visibility::Level level,
const StringPiece& comment) {
- Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
+ std::optional<ResourceTable::SearchResult> result = table.FindResource(name);
if (!result) {
return ::testing::AssertionFailure() << "no resource '" << name << "' found in table";
}
@@ -242,7 +242,7 @@ TEST(ResourceTableTest, SetAllowNew) {
const ResourceName name = test::ParseNameOrDie("android:string/foo");
AllowNew allow_new;
- Maybe<ResourceTable::SearchResult> result;
+ std::optional<ResourceTable::SearchResult> result;
allow_new.comment = "first";
ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetAllowNew(allow_new).Build(),
@@ -274,7 +274,7 @@ TEST(ResourceTableTest, SetOverlayable) {
const ResourceName name = test::ParseNameOrDie("android:string/foo");
ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetOverlayable(overlayable_item).Build(),
test::GetDiagnostics()));
- Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
+ std::optional<ResourceTable::SearchResult> search_result = table.FindResource(name);
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index e0e80ac02dea..23f6c88aad91 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -40,8 +40,7 @@ using ::android::base::StringPrintf;
namespace aapt {
namespace ResourceUtils {
-Maybe<ResourceName> ToResourceName(
- const android::ResTable::resource_name& name_in) {
+std::optional<ResourceName> ToResourceName(const android::ResTable::resource_name& name_in) {
// TODO: Remove this when ResTable and AssetManager(1) are removed from AAPT2
ResourceName name_out;
if (!name_in.package) {
@@ -51,12 +50,11 @@ Maybe<ResourceName> ToResourceName(
name_out.package =
util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
- const ResourceType* type;
+ std::optional<ResourceNamedTypeRef> type;
if (name_in.type) {
- type = ParseResourceType(
- util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
+ type = ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
} else if (name_in.type8) {
- type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
+ type = ParseResourceNamedType(StringPiece(name_in.type8, name_in.typeLen));
} else {
return {};
}
@@ -65,7 +63,7 @@ Maybe<ResourceName> ToResourceName(
return {};
}
- name_out.type = *type;
+ name_out.type = type->ToResourceNamedType();
if (name_in.name) {
name_out.entry =
@@ -78,7 +76,7 @@ Maybe<ResourceName> ToResourceName(
return name_out;
}
-Maybe<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) {
+std::optional<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) {
ResourceName name_out;
if (!name_in.package) {
return {};
@@ -86,12 +84,12 @@ Maybe<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& n
name_out.package = std::string(name_in.package, name_in.package_len);
- const ResourceType* type;
+ std::optional<ResourceNamedTypeRef> type;
if (name_in.type16) {
- type = ParseResourceType(
- util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
+ type =
+ ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
} else if (name_in.type) {
- type = ParseResourceType(StringPiece(name_in.type, name_in.type_len));
+ type = ParseResourceNamedType(StringPiece(name_in.type, name_in.type_len));
} else {
return {};
}
@@ -100,7 +98,7 @@ Maybe<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& n
return {};
}
- name_out.type = *type;
+ name_out.type = type->ToResourceNamedType();
if (name_in.entry16) {
name_out.entry =
@@ -134,7 +132,7 @@ bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
return false;
}
- const ResourceType* parsed_type = ParseResourceType(type);
+ std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(type);
if (!parsed_type) {
return false;
}
@@ -182,7 +180,7 @@ bool ParseReference(const StringPiece& str, ResourceNameRef* out_ref,
return false;
}
- if (create && name.type != ResourceType::kId) {
+ if (create && name.type.type != ResourceType::kId) {
return false;
}
@@ -231,7 +229,7 @@ bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) {
if (out_ref) {
out_ref->package = package;
- out_ref->type = ResourceType::kAttr;
+ out_ref->type = ResourceNamedTypeWithDefaultName(ResourceType::kAttr);
out_ref->entry = entry;
}
return true;
@@ -251,8 +249,7 @@ bool IsAttributeReference(const StringPiece& str) {
* <[*]package>:[style/]<entry>
* [[*]package:style/]<entry>
*/
-Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
- std::string* out_error) {
+std::optional<Reference> ParseStyleParentReference(const StringPiece& str, std::string* out_error) {
if (str.empty()) {
return {};
}
@@ -274,7 +271,7 @@ Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
}
ResourceNameRef ref;
- ref.type = ResourceType::kStyle;
+ ref.type = ResourceNamedTypeWithDefaultName(ResourceType::kStyle);
StringPiece type_str;
android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
@@ -301,7 +298,7 @@ Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
return result;
}
-Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
+std::optional<Reference> ParseXmlAttributeName(const StringPiece& str) {
StringPiece trimmed_str = util::TrimWhitespace(str);
const char* start = trimmed_str.data();
const char* const end = start + trimmed_str.size();
@@ -325,8 +322,9 @@ Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
p++;
}
- ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
- return Maybe<Reference>(std::move(ref));
+ ref.name = ResourceName(package, ResourceNamedTypeWithDefaultName(ResourceType::kAttr),
+ name.empty() ? trimmed_str : name);
+ return std::optional<Reference>(std::move(ref));
}
std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
@@ -488,18 +486,18 @@ std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
: util::make_unique<BinaryPrimitive>(value);
}
-Maybe<bool> ParseBool(const StringPiece& str) {
+std::optional<bool> ParseBool(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
- return Maybe<bool>(true);
+ return std::optional<bool>(true);
} else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
trimmed_str == "False") {
- return Maybe<bool>(false);
+ return std::optional<bool>(false);
}
return {};
}
-Maybe<uint32_t> ParseInt(const StringPiece& str) {
+std::optional<uint32_t> ParseInt(const StringPiece& str) {
std::u16string str16 = util::Utf8ToUtf16(str);
android::Res_value value;
if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
@@ -508,7 +506,7 @@ Maybe<uint32_t> ParseInt(const StringPiece& str) {
return {};
}
-Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
+std::optional<ResourceId> ParseResourceId(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
@@ -524,7 +522,7 @@ Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
return {};
}
-Maybe<int> ParseSdkVersion(const StringPiece& str) {
+std::optional<int> ParseSdkVersion(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
@@ -534,7 +532,7 @@ Maybe<int> ParseSdkVersion(const StringPiece& str) {
}
// Try parsing the code name.
- Maybe<int> entry = GetDevelopmentSdkCodeNameVersion(trimmed_str);
+ std::optional<int> entry = GetDevelopmentSdkCodeNameVersion(trimmed_str);
if (entry) {
return entry.value();
}
@@ -551,7 +549,7 @@ Maybe<int> ParseSdkVersion(const StringPiece& str) {
}
std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
- if (Maybe<bool> maybe_result = ParseBool(str)) {
+ if (std::optional<bool> maybe_result = ParseBool(str)) {
const uint32_t data = maybe_result.value() ? 0xffffffffu : 0u;
return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, data);
}
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index be493db8cee0..fe450a834dfa 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -75,35 +75,33 @@ bool IsAttributeReference(const android::StringPiece& str);
/**
* Convert an android::ResTable::resource_name to an aapt::ResourceName struct.
*/
-Maybe<ResourceName> ToResourceName(
- const android::ResTable::resource_name& name);
+std::optional<ResourceName> ToResourceName(const android::ResTable::resource_name& name);
/**
* Convert an android::AssetManager2::ResourceName to an aapt::ResourceName struct.
*/
-Maybe<ResourceName> ToResourceName(
- const android::AssetManager2::ResourceName& name_in);
+std::optional<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in);
/**
* Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
* false, or False.
*/
-Maybe<bool> ParseBool(const android::StringPiece& str);
+std::optional<bool> ParseBool(const android::StringPiece& str);
/**
* Returns a uint32_t if the string is an integer.
*/
-Maybe<uint32_t> ParseInt(const android::StringPiece& str);
+std::optional<uint32_t> ParseInt(const android::StringPiece& str);
/**
* Returns an ID if it the string represented a valid ID.
*/
-Maybe<ResourceId> ParseResourceId(const android::StringPiece& str);
+std::optional<ResourceId> ParseResourceId(const android::StringPiece& str);
/**
* Parses an SDK version, which can be an integer, or a letter from A-Z.
*/
-Maybe<int> ParseSdkVersion(const android::StringPiece& str);
+std::optional<int> ParseSdkVersion(const android::StringPiece& str);
/*
* Returns a Reference, or None Maybe instance if the string `str` was parsed as
@@ -116,7 +114,8 @@ Maybe<int> ParseSdkVersion(const android::StringPiece& str);
* ?[package:]style/<entry> or
* <package>:[style/]<entry>
*/
-Maybe<Reference> ParseStyleParentReference(const android::StringPiece& str, std::string* out_error);
+std::optional<Reference> ParseStyleParentReference(const android::StringPiece& str,
+ std::string* out_error);
/*
* Returns a Reference if the string `str` was parsed as a valid XML attribute
@@ -125,7 +124,7 @@ Maybe<Reference> ParseStyleParentReference(const android::StringPiece& str, std:
*
* package:entry
*/
-Maybe<Reference> ParseXmlAttributeName(const android::StringPiece& str);
+std::optional<Reference> ParseXmlAttributeName(const android::StringPiece& str);
/*
* Returns a Reference object if the string was parsed as a resource or
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index b08bf9a1ff17..1aaa34deee79 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -30,15 +30,15 @@ using ::testing::Pointee;
namespace aapt {
TEST(ResourceUtilsTest, ParseBool) {
- EXPECT_THAT(ResourceUtils::ParseBool("true"), Eq(Maybe<bool>(true)));
- EXPECT_THAT(ResourceUtils::ParseBool("TRUE"), Eq(Maybe<bool>(true)));
- EXPECT_THAT(ResourceUtils::ParseBool("True"), Eq(Maybe<bool>(true)));
+ EXPECT_THAT(ResourceUtils::ParseBool("true"), Eq(std::optional<bool>(true)));
+ EXPECT_THAT(ResourceUtils::ParseBool("TRUE"), Eq(std::optional<bool>(true)));
+ EXPECT_THAT(ResourceUtils::ParseBool("True"), Eq(std::optional<bool>(true)));
- EXPECT_THAT(ResourceUtils::ParseBool("false"), Eq(Maybe<bool>(false)));
- EXPECT_THAT(ResourceUtils::ParseBool("FALSE"), Eq(Maybe<bool>(false)));
- EXPECT_THAT(ResourceUtils::ParseBool("False"), Eq(Maybe<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool("false"), Eq(std::optional<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool("FALSE"), Eq(std::optional<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool("False"), Eq(std::optional<bool>(false)));
- EXPECT_THAT(ResourceUtils::ParseBool(" False\n "), Eq(Maybe<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool(" False\n "), Eq(std::optional<bool>(false)));
}
TEST(ResourceUtilsTest, ParseResourceName) {
@@ -155,41 +155,42 @@ TEST(ResourceUtilsTest, ParseStyleParentReference) {
const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo");
std::string err_str;
- Maybe<Reference> ref = ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
+ std::optional<Reference> ref =
+ ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("@style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("?android:style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("?style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("android:style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("android:foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("@android:foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("*android:style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
EXPECT_TRUE(ref.value().private_reference);
}
@@ -228,15 +229,11 @@ TEST(ResourceUtilsTest, ItemsWithWhitespaceAreParsedCorrectly) {
}
TEST(ResourceUtilsTest, ParseSdkVersionWithCodename) {
- EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q"), Eq(Maybe<int>(10000)));
- EXPECT_THAT(
- ResourceUtils::ParseSdkVersion("Q.fingerprint"),
- Eq(Maybe<int>(10000)));
-
- EXPECT_THAT(ResourceUtils::ParseSdkVersion("R"), Eq(Maybe<int>(10000)));
- EXPECT_THAT(
- ResourceUtils::ParseSdkVersion("R.fingerprint"),
- Eq(Maybe<int>(10000)));
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q"), Eq(std::optional<int>(10000)));
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q.fingerprint"), Eq(std::optional<int>(10000)));
+
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("R"), Eq(std::optional<int>(10000)));
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("R.fingerprint"), Eq(std::optional<int>(10000)));
}
TEST(ResourceUtilsTest, StringBuilderWhitespaceRemoval) {
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 2a90f267f185..b796eb07f076 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -116,11 +116,11 @@ bool Reference::Equals(const Value* value) const {
}
bool Reference::Flatten(android::Res_value* out_value) const {
- if (name && name.value().type == ResourceType::kMacro) {
+ if (name && name.value().type.type == ResourceType::kMacro) {
return false;
}
- const ResourceId resid = id.value_or_default(ResourceId(0));
+ const ResourceId resid = id.value_or(ResourceId(0));
const bool dynamic = resid.is_valid() && is_dynamic;
if (reference_type == Reference::Type::kResource) {
@@ -192,7 +192,7 @@ static void PrettyPrintReferenceImpl(const Reference& ref, bool print_package, P
if (print_package) {
printer->Print(name.to_string());
} else {
- printer->Print(to_string(name.type));
+ printer->Print(name.type.to_string());
printer->Print("/");
printer->Print(name.entry);
}
@@ -1040,7 +1040,7 @@ void Macro::Print(std::ostream* out) const {
}
bool operator<(const Reference& a, const Reference& b) {
- int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
+ int cmp = a.name.value_or(ResourceName{}).compare(b.name.value_or(ResourceName{}));
if (cmp != 0) return cmp < 0;
return a.id < b.id;
}
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index d903b7e1b8b3..1694d6b6fe4a 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -31,7 +31,6 @@
#include "ValueTransformer.h"
#include "io/File.h"
#include "text/Printer.h"
-#include "util/Maybe.h"
namespace aapt {
@@ -159,8 +158,8 @@ struct Reference : public TransformableItem<Reference, BaseItem<Reference>> {
kAttribute,
};
- Maybe<ResourceName> name;
- Maybe<ResourceId> id;
+ std::optional<ResourceName> name;
+ std::optional<ResourceId> id;
std::optional<uint32_t> type_flags;
Reference::Type reference_type;
bool private_reference = false;
@@ -327,7 +326,7 @@ struct Style : public TransformableValue<Style, BaseValue<Style>> {
friend std::ostream& operator<<(std::ostream& out, const Entry& entry);
};
- Maybe<Reference> parent;
+ std::optional<Reference> parent;
// If set to true, the parent was auto inferred from the style's name.
bool parent_inferred = false;
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index c557f3c77654..2c55d1d548db 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -18,6 +18,9 @@
#include "test/Test.h"
+using ::testing::Eq;
+using ::testing::Optional;
+
namespace aapt {
TEST(ResourceTypeTest, ParseResourceTypes) {
@@ -125,4 +128,104 @@ TEST(ResourceTypeTest, ParseResourceTypes) {
EXPECT_EQ(type, nullptr);
}
+TEST(ResourceTypeTest, ParseResourceNamedType) {
+ auto type = ParseResourceNamedType("anim");
+ EXPECT_THAT(type, Optional(Eq(ResourceNamedType("anim", ResourceType::kAnim))));
+
+ type = ParseResourceNamedType("layout");
+ EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout", ResourceType::kLayout))));
+
+ type = ParseResourceNamedType("layout:2");
+ EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:2", ResourceType::kLayout))));
+
+ type = ParseResourceNamedType("layout:another");
+ EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:another", ResourceType::kLayout))));
+
+ type = ParseResourceNamedType("layout:");
+ EXPECT_THAT(type, Eq(std::nullopt));
+
+ type = ParseResourceNamedType("layout2");
+ EXPECT_THAT(type, Eq(std::nullopt));
+
+ type = ParseResourceNamedType("blahaha");
+ EXPECT_THAT(type, Eq(std::nullopt));
+}
+
+TEST(ResourceTypeTest, ResourceNamedTypeWithDefaultName) {
+ auto type = ResourceNamedTypeWithDefaultName(ResourceType::kAnim);
+ EXPECT_THAT(type, Eq(ResourceNamedType("anim", ResourceType::kAnim)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kAnimator);
+ EXPECT_THAT(type, Eq(ResourceNamedType("animator", ResourceType::kAnimator)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kArray);
+ EXPECT_THAT(type, Eq(ResourceNamedType("array", ResourceType::kArray)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kAttr);
+ EXPECT_THAT(type, Eq(ResourceNamedType("attr", ResourceType::kAttr)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kAttrPrivate);
+ EXPECT_THAT(type, Eq(ResourceNamedType("^attr-private", ResourceType::kAttrPrivate)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kBool);
+ EXPECT_THAT(type, Eq(ResourceNamedType("bool", ResourceType::kBool)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kColor);
+ EXPECT_THAT(type, Eq(ResourceNamedType("color", ResourceType::kColor)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kConfigVarying);
+ EXPECT_THAT(type, Eq(ResourceNamedType("configVarying", ResourceType::kConfigVarying)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kDimen);
+ EXPECT_THAT(type, Eq(ResourceNamedType("dimen", ResourceType::kDimen)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kDrawable);
+ EXPECT_THAT(type, Eq(ResourceNamedType("drawable", ResourceType::kDrawable)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kFont);
+ EXPECT_THAT(type, Eq(ResourceNamedType("font", ResourceType::kFont)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kFraction);
+ EXPECT_THAT(type, Eq(ResourceNamedType("fraction", ResourceType::kFraction)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kId);
+ EXPECT_THAT(type, Eq(ResourceNamedType("id", ResourceType::kId)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kInteger);
+ EXPECT_THAT(type, Eq(ResourceNamedType("integer", ResourceType::kInteger)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kInterpolator);
+ EXPECT_THAT(type, Eq(ResourceNamedType("interpolator", ResourceType::kInterpolator)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kLayout);
+ EXPECT_THAT(type, Eq(ResourceNamedType("layout", ResourceType::kLayout)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kMenu);
+ EXPECT_THAT(type, Eq(ResourceNamedType("menu", ResourceType::kMenu)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kMipmap);
+ EXPECT_THAT(type, Eq(ResourceNamedType("mipmap", ResourceType::kMipmap)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kNavigation);
+ EXPECT_THAT(type, Eq(ResourceNamedType("navigation", ResourceType::kNavigation)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kPlurals);
+ EXPECT_THAT(type, Eq(ResourceNamedType("plurals", ResourceType::kPlurals)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kRaw);
+ EXPECT_THAT(type, Eq(ResourceNamedType("raw", ResourceType::kRaw)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kString);
+ EXPECT_THAT(type, Eq(ResourceNamedType("string", ResourceType::kString)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kStyle);
+ EXPECT_THAT(type, Eq(ResourceNamedType("style", ResourceType::kStyle)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kTransition);
+ EXPECT_THAT(type, Eq(ResourceNamedType("transition", ResourceType::kTransition)));
+
+ type = ResourceNamedTypeWithDefaultName(ResourceType::kXml);
+ EXPECT_THAT(type, Eq(ResourceNamedType("xml", ResourceType::kXml)));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index f8e448f0f676..34e8edb0a47f 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -26,9 +26,8 @@ using android::StringPiece;
namespace aapt {
static ApiVersion sDevelopmentSdkLevel = 10000;
-static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>({
- "Q", "R", "S", "Sv2"
-});
+static const auto sDevelopmentSdkCodeNames =
+ std::unordered_set<StringPiece>({"Q", "R", "S", "Sv2", "Tiramisu", "UpsideDownCake"});
static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
{0x021c, 1},
@@ -78,9 +77,10 @@ ApiVersion FindAttributeSdkLevel(const ResourceId& id) {
return iter->second;
}
-Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const StringPiece& code_name) {
+std::optional<ApiVersion> GetDevelopmentSdkCodeNameVersion(const StringPiece& code_name) {
return (sDevelopmentSdkCodeNames.find(code_name) == sDevelopmentSdkCodeNames.end())
- ? Maybe<ApiVersion>() : sDevelopmentSdkLevel;
+ ? std::optional<ApiVersion>()
+ : sDevelopmentSdkLevel;
}
} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 0f27ac50b3bf..0bd61c04f2b2 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -58,10 +58,12 @@ enum : ApiVersion {
SDK_R = 30,
SDK_S = 31,
SDK_S_V2 = 32,
+ SDK_TIRAMISU = 33,
+ SDK_CUR_DEVELOPMENT = 10000,
};
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
-Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const android::StringPiece& code_name);
+std::optional<ApiVersion> GetDevelopmentSdkCodeNameVersion(const android::StringPiece& code_name);
} // namespace aapt
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 92934c343960..4f9369a5d517 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -17,21 +17,20 @@
#ifndef AAPT_SOURCE_H
#define AAPT_SOURCE_H
+#include <optional>
#include <ostream>
#include <string>
#include "android-base/stringprintf.h"
#include "androidfw/StringPiece.h"
-#include "util/Maybe.h"
-
namespace aapt {
// Represents a file on disk. Used for logging and showing errors.
struct Source {
std::string path;
- Maybe<size_t> line;
- Maybe<std::string> archive;
+ std::optional<size_t> line;
+ std::optional<std::string> archive;
Source() = default;
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
index 919b4c98fa8f..b1452fad0e8f 100644
--- a/tools/aapt2/cmd/Command.cpp
+++ b/tools/aapt2/cmd/Command.cpp
@@ -72,7 +72,7 @@ void Command::AddRequiredFlagList(const StringPiece& name, const StringPiece& de
}
void Command::AddOptionalFlag(const StringPiece& name, const StringPiece& description,
- Maybe<std::string>* value, uint32_t flags) {
+ std::optional<std::string>* value, uint32_t flags) {
auto func = [value, flags](const StringPiece& arg) -> bool {
*value = (flags & Command::kPath) ? GetSafePath(arg) : arg.to_string();
return true;
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
index d21571d530d2..8678cda59856 100644
--- a/tools/aapt2/cmd/Command.h
+++ b/tools/aapt2/cmd/Command.h
@@ -18,6 +18,7 @@
#define AAPT_COMMAND_H
#include <functional>
+#include <optional>
#include <ostream>
#include <string>
#include <unordered_set>
@@ -25,19 +26,20 @@
#include "androidfw/StringPiece.h"
-#include "util/Maybe.h"
-
namespace aapt {
class Command {
public:
- explicit Command(const android::StringPiece& name) : name_(name.to_string()),
- short_name_(""),
- full_subcommand_name_(name.to_string()) {}
+ explicit Command(const android::StringPiece& name)
+ : name_(name.to_string()), full_subcommand_name_(name.to_string()){};
explicit Command(const android::StringPiece& name, const android::StringPiece& short_name)
- : name_(name.to_string()), short_name_(short_name.to_string()),
- full_subcommand_name_(name.to_string()) {}
+ : name_(name.to_string()),
+ short_name_(short_name.to_string()),
+ full_subcommand_name_(name.to_string()){};
+
+ Command(Command&&) = default;
+ Command& operator=(Command&&) = default;
virtual ~Command() = default;
@@ -58,7 +60,7 @@ class Command {
uint32_t flags = 0);
void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
- Maybe<std::string>* value, uint32_t flags = 0);
+ std::optional<std::string>* value, uint32_t flags = 0);
void AddOptionalFlagList(const android::StringPiece& name,
const android::StringPiece& description, std::vector<std::string>* value,
@@ -87,8 +89,6 @@ class Command {
virtual int Action(const std::vector<std::string>& args) = 0;
private:
- DISALLOW_COPY_AND_ASSIGN(Command);
-
struct Flag {
explicit Flag(const android::StringPiece& name, const android::StringPiece& description,
const bool is_required, const size_t num_args,
@@ -104,8 +104,8 @@ class Command {
bool found = false;
};
- const std::string name_;
- const std::string short_name_;
+ std::string name_;
+ std::string short_name_;
std::string description_ = "";
std::string full_subcommand_name_;
diff --git a/tools/aapt2/cmd/Command_test.cpp b/tools/aapt2/cmd/Command_test.cpp
index 65608fdf64a7..7aa1aa017f7b 100644
--- a/tools/aapt2/cmd/Command_test.cpp
+++ b/tools/aapt2/cmd/Command_test.cpp
@@ -38,7 +38,7 @@ TEST(CommandTest, LongFullyQualifiedPathWindows) {
TestCommand command;
std::string required_flag;
command.AddRequiredFlag("--rflag", "", &required_flag, Command::kPath);
- Maybe<std::string> optional_flag;
+ std::optional<std::string> optional_flag;
command.AddOptionalFlag("--oflag", "", &optional_flag, Command::kPath);
std::vector<std::string> required_flag_list;
command.AddRequiredFlagList("--rlflag", "", &required_flag_list, Command::kPath);
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index cd5015e81203..fe560180bd48 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -47,7 +47,6 @@
#include "io/ZipArchive.h"
#include "trace/TraceBuffer.h"
#include "util/Files.h"
-#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
#include "xml/XmlPullParser.h"
@@ -75,10 +74,10 @@ struct ResourcePathData {
};
// Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
- const char dir_sep,
- std::string* out_error,
- const CompileOptions& options) {
+static std::optional<ResourcePathData> ExtractResourcePathData(const std::string& path,
+ const char dir_sep,
+ std::string* out_error,
+ const CompileOptions& options) {
std::vector<std::string> parts = util::Split(path, dir_sep);
if (parts.size() < 2) {
if (out_error) *out_error = "bad resource path";
@@ -337,7 +336,7 @@ static bool IsValidFile(IAaptContext* context, const std::string& input_path) {
if (file_type == file::FileType::kDirectory) {
context->GetDiagnostics()->Error(DiagMessage(input_path)
<< "resource file cannot be a directory");
- } else if (file_type == file::FileType::kNonexistant) {
+ } else if (file_type == file::FileType::kNonExistant) {
context->GetDiagnostics()->Error(DiagMessage(input_path) << "file not found");
} else {
context->GetDiagnostics()->Error(DiagMessage(input_path)
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 1bc1f6651f85..bd2e3d72a551 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -17,7 +17,10 @@
#ifndef AAPT2_COMPILE_H
#define AAPT2_COMPILE_H
-#include "androidfw/StringPiece.h"
+#include <optional>
+
+#include <androidfw/StringPiece.h>
+
#include "format/Archive.h"
#include "process/IResourceTableConsumer.h"
#include "Command.h"
@@ -28,11 +31,11 @@ namespace aapt {
struct CompileOptions {
std::string output_path;
- Maybe<std::string> source_path;
- Maybe<std::string> res_dir;
- Maybe<std::string> res_zip;
- Maybe<std::string> generate_text_symbols_path;
- Maybe<Visibility::Level> visibility;
+ std::optional<std::string> source_path;
+ std::optional<std::string> res_dir;
+ std::optional<std::string> res_zip;
+ std::optional<std::string> generate_text_symbols_path;
+ std::optional<Visibility::Level> visibility;
bool pseudolocalize = false;
bool no_png_crunch = false;
bool legacy_mode = false;
@@ -80,8 +83,8 @@ class CompileCommand : public Command {
private:
IDiagnostics* diagnostic_;
CompileOptions options_;
- Maybe<std::string> visibility_;
- Maybe<std::string> trace_folder_;
+ std::optional<std::string> visibility_;
+ std::optional<std::string> trace_folder_;
};
int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* output_writer,
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 89757134f3a7..fbfbf68b30bb 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -19,12 +19,11 @@
#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "android-base/utf8.h"
-
+#include "format/proto/ProtoDeserialize.h"
#include "io/StringStream.h"
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
#include "test/Test.h"
-#include "format/proto/ProtoDeserialize.h"
namespace aapt {
@@ -59,55 +58,56 @@ TEST_F(CompilerTest, MultiplePeriods) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
"integration-tests", "CompileTest", "res"});
+ const std::string kOutDir = testing::TempDir();
// Resource files without periods in the file name should not throw errors
const std::string path0 = BuildPath({kResDir, "values", "values.xml"});
- const std::string path0_out = BuildPath({kResDir, "values_values.arsc.flat"});
+ const std::string path0_out = BuildPath({kOutDir, "values_values.arsc.flat"});
::android::base::utf8::unlink(path0_out.c_str());
- ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ false, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
const std::string path1 = BuildPath({kResDir, "drawable", "image.png"});
- const std::string path1_out = BuildPath({kResDir, "drawable_image.png.flat"});
+ const std::string path1_out = BuildPath({kOutDir, "drawable_image.png.flat"});
::android::base::utf8::unlink(path1_out.c_str());
- ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ false, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
const std::string path2 = BuildPath({kResDir, "drawable", "image.9.png"});
- const std::string path2_out = BuildPath({kResDir, "drawable_image.9.png.flat"});
+ const std::string path2_out = BuildPath({kOutDir, "drawable_image.9.png.flat"});
::android::base::utf8::unlink(path2_out.c_str());
- ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ false, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
// Resource files with periods in the file name should fail on non-legacy compilations
const std::string path3 = BuildPath({kResDir, "values", "values.all.xml"});
- const std::string path3_out = BuildPath({kResDir, "values_values.all.arsc.flat"});
+ const std::string path3_out = BuildPath({kOutDir, "values_values.all.arsc.flat"});
::android::base::utf8::unlink(path3_out.c_str());
- ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_NE(TestCompile(path3, kOutDir, /** legacy */ false, diag), 0);
ASSERT_NE(::android::base::utf8::unlink(path3_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path3, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path3_out.c_str()), 0);
const std::string path4 = BuildPath({kResDir, "drawable", "image.small.png"});
- const std::string path4_out = BuildPath({kResDir, "drawable_image.small.png.flat"});
+ const std::string path4_out = BuildPath({kOutDir, "drawable_image.small.png.flat"});
::android::base::utf8::unlink(path4_out.c_str());
- ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_NE(TestCompile(path4, kOutDir, /** legacy */ false, diag), 0);
ASSERT_NE(::android::base::utf8::unlink(path4_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path4, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path4_out.c_str()), 0);
const std::string path5 = BuildPath({kResDir, "drawable", "image.small.9.png"});
- const std::string path5_out = BuildPath({kResDir, "drawable_image.small.9.png.flat"});
+ const std::string path5_out = BuildPath({kOutDir, "drawable_image.small.9.png.flat"});
::android::base::utf8::unlink(path5_out.c_str());
- ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_NE(TestCompile(path5, kOutDir, /** legacy */ false, diag), 0);
ASSERT_NE(::android::base::utf8::unlink(path5_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path5, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0);
}
@@ -116,9 +116,7 @@ TEST_F(CompilerTest, DirInput) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
"integration-tests", "CompileTest", "DirInput", "res"});
- const std::string kOutputFlata =
- BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
- "CompileTest", "DirInput", "compiled.flata"});
+ const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
::android::base::utf8::unlink(kOutputFlata.c_str());
std::vector<android::StringPiece> args;
@@ -147,9 +145,7 @@ TEST_F(CompilerTest, ZipInput) {
const std::string kResZip =
BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
"CompileTest", "ZipInput", "res.zip"});
- const std::string kOutputFlata =
- BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
- "CompileTest", "ZipInput", "compiled.flata"});
+ const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
::android::base::utf8::unlink(kOutputFlata.c_str());
@@ -257,9 +253,9 @@ TEST_F(CompilerTest, DoNotTranslateTest) {
TEST_F(CompilerTest, RelativePathTest) {
StdErrDiagnostics diag;
- const std::string res_path = BuildPath(
- {android::base::Dirname(android::base::GetExecutablePath()),
- "integration-tests", "CompileTest", "res"});
+ const std::string res_path =
+ BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+ "CompileTest", "res"});
const std::string path_values_colors = GetTestPath("values/colors.xml");
WriteFile(path_values_colors, "<resources>"
@@ -272,9 +268,8 @@ TEST_F(CompilerTest, RelativePathTest) {
"<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
"</LinearLayout>");
- const std::string compiled_files_dir = BuildPath(
- {android::base::Dirname(android::base::GetExecutablePath()),
- "integration-tests", "CompileTest", "compiled"});
+ const std::string compiled_files_dir =
+ BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "compiled"});
CHECK(file::mkdirs(compiled_files_dir.data()));
const std::string path_values_colors_out =
@@ -283,9 +278,8 @@ TEST_F(CompilerTest, RelativePathTest) {
BuildPath({compiled_files_dir, "layout_layout_one.flat"});
::android::base::utf8::unlink(path_values_colors_out.c_str());
::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
- const std::string apk_path = BuildPath(
- {android::base::Dirname(android::base::GetExecutablePath()),
- "integration-tests", "CompileTest", "out.apk"});
+ const std::string apk_path =
+ BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "out.apk"});
const std::string source_set_res = BuildPath({"main", "res"});
const std::string relative_path_values_colors =
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 22bcd8589ce9..3b097e09e09d 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -367,8 +367,7 @@ int ConvertCommand::Action(const std::vector<std::string>& args) {
return 1;
}
- Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(),
- context.GetDiagnostics());
+ auto app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
if (!app_info) {
return 1;
}
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
index 7e2029dfc4d2..2cdb0c87310f 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -17,6 +17,8 @@
#ifndef AAPT2_CONVERT_H
#define AAPT2_CONVERT_H
+#include <optional>
+
#include "Command.h"
#include "LoadedApk.h"
#include "format/binary/TableFlattener.h"
@@ -52,7 +54,7 @@ class ConvertCommand : public Command {
TableFlattenerOptions table_flattener_options_;
XmlFlattenerOptions xml_flattener_options_;
std::string output_path_;
- Maybe<std::string> output_format_;
+ std::optional<std::string> output_format_;
bool verbose_ = false;
};
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 3950f337b575..d9e8c921dbc5 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -87,8 +87,8 @@ static bool IsSymbolVisibilityDifferent(const Visibility& vis_a, const Visibilit
}
template <typename Id>
-static bool IsIdDiff(const Visibility::Level& level_a, const Maybe<Id>& id_a,
- const Visibility::Level& level_b, const Maybe<Id>& id_b) {
+static bool IsIdDiff(const Visibility::Level& level_a, const std::optional<Id>& id_a,
+ const Visibility::Level& level_b, const std::optional<Id>& id_b) {
if (level_a == Visibility::Level::kPublic || level_b == Visibility::Level::kPublic) {
return id_a != id_b;
}
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 3982d12f6036..bcce3e5a800f 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -257,12 +257,11 @@ int DumpConfigsCommand::Dump(LoadedApk* apk) {
}
int DumpPackageNameCommand::Dump(LoadedApk* apk) {
- Maybe<std::string> package_name = GetPackageName(apk);
- if (!package_name) {
+ auto package_name = GetPackageName(apk);
+ if (!package_name.has_value()) {
return 1;
}
-
- GetPrinter()->Println(package_name.value());
+ GetPrinter()->Println(*package_name);
return 0;
}
@@ -283,12 +282,12 @@ int DumpStringsCommand::Dump(LoadedApk* apk) {
}
int DumpStyleParentCommand::Dump(LoadedApk* apk) {
- Maybe<std::string> package_name = GetPackageName(apk);
- if (!package_name) {
+ auto package_name = GetPackageName(apk);
+ if (!package_name.has_value()) {
return 1;
}
- const auto target_style = ResourceName(package_name.value(), ResourceType::kStyle, style_);
+ const auto target_style = ResourceName(*package_name, ResourceType::kStyle, style_);
const auto table = apk->GetResourceTable();
if (!table) {
@@ -296,7 +295,7 @@ int DumpStyleParentCommand::Dump(LoadedApk* apk) {
return 1;
}
- Maybe<ResourceTable::SearchResult> target = table->FindResource(target_style);
+ std::optional<ResourceTable::SearchResult> target = table->FindResource(target_style);
if (!target) {
GetDiagnostics()->Error(
DiagMessage() << "Target style \"" << target_style.entry << "\" does not exist");
@@ -561,4 +560,21 @@ const char DumpBadgerCommand::kBadgerData[2925] = {
32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10};
+int DumpChunks::Dump(LoadedApk* apk) {
+ auto file = apk->GetFileCollection()->FindFile("resources.arsc");
+ if (!file) {
+ GetDiagnostics()->Error(DiagMessage() << "Failed to find resources.arsc in APK");
+ return 1;
+ }
+
+ auto data = file->OpenAsData();
+ if (!data) {
+ GetDiagnostics()->Error(DiagMessage() << "Failed to open resources.arsc ");
+ return 1;
+ }
+
+ Debug::DumpChunks(data->data(), data->size(), GetPrinter(), GetDiagnostics());
+ return 0;
+}
+
} // namespace aapt
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index cd51f7a7718c..ec320ecd2a32 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -17,6 +17,9 @@
#ifndef AAPT2_DUMP_H
#define AAPT2_DUMP_H
+#include <io/FileStream.h>
+#include <io/ZipArchive.h>
+
#include "Command.h"
#include "Debug.h"
#include "LoadedApk.h"
@@ -43,17 +46,17 @@ class DumpApkCommand : public Command {
return diag_;
}
- Maybe<std::string> GetPackageName(LoadedApk* apk) {
+ std::optional<std::string> GetPackageName(LoadedApk* apk) {
xml::Element* manifest_el = apk->GetManifest()->root.get();
if (!manifest_el) {
GetDiagnostics()->Error(DiagMessage() << "No AndroidManifest.");
- return Maybe<std::string>();
+ return {};
}
xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
if (!attr) {
GetDiagnostics()->Error(DiagMessage() << "No package name.");
- return Maybe<std::string>();
+ return {};
}
return attr->value;
}
@@ -226,6 +229,16 @@ class DumpXmlStringsCommand : public DumpApkCommand {
std::vector<std::string> files_;
};
+class DumpChunks : public DumpApkCommand {
+ public:
+ DumpChunks(text::Printer* printer, IDiagnostics* diag) : DumpApkCommand("chunks", printer, diag) {
+ SetDescription("Print the chunk information of the compiled resources.arsc in the APK.");
+ }
+
+ int Dump(LoadedApk* apk) override;
+};
+
+/** Prints the tree of a compiled xml in an APK. */
class DumpXmlTreeCommand : public DumpApkCommand {
public:
explicit DumpXmlTreeCommand(text::Printer* printer, IDiagnostics* diag)
@@ -263,6 +276,7 @@ class DumpCommand : public Command {
AddOptionalSubcommand(util::make_unique<DumpStringsCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpStyleParentCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpTableCommand>(printer, diag_));
+ AddOptionalSubcommand(util::make_unique<DumpChunks>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpOverlayableCommand>(printer, diag_));
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e4d0f3b6bd23..790f2b34c58b 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -207,7 +207,7 @@ class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
}
// Check to see if this is an 'id' with the target package.
- if (name.type == ResourceType::kId && symbol->id) {
+ if (name.type.type == ResourceType::kId && symbol->id) {
ResourceId* id = &symbol->id.value();
if (id->package_id() > kAppPackageId) {
// Rewrite the resource ID to be compatible pre-O.
@@ -299,7 +299,7 @@ struct ResourceFileFlattenerOptions {
bool do_not_fail_on_missing_resources = false;
OutputFormat output_format = OutputFormat::kApk;
std::unordered_set<std::string> extensions_to_not_compress;
- Maybe<std::regex> regex_to_not_compress;
+ std::optional<std::regex> regex_to_not_compress;
};
// A sampling of public framework resource IDs.
@@ -741,7 +741,7 @@ static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path,
const size_t res_id_str_len = line.size() - res_id_start_idx;
StringPiece res_id_str = util::TrimWhitespace(line.substr(res_id_start_idx, res_id_str_len));
- Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str);
+ std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str);
if (!maybe_id) {
diag->Error(DiagMessage(Source(path, line_no)) << "invalid resource ID '" << res_id_str
<< "'");
@@ -793,7 +793,7 @@ class Linker {
if (!options_.manifest_fixer_options.compile_sdk_version) {
xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
if (attr != nullptr) {
- Maybe<std::string>& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
+ auto& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) {
switch (prim->value.dataType) {
case Res_value::TYPE_INT_DEC:
@@ -816,7 +816,7 @@ class Linker {
if (!options_.manifest_fixer_options.compile_sdk_version_codename) {
xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName");
if (attr != nullptr) {
- Maybe<std::string>& compile_sdk_version_codename =
+ std::optional<std::string>& compile_sdk_version_codename =
options_.manifest_fixer_options.compile_sdk_version_codename;
if (String* str = ValueCast<String>(attr->compiled_value.get())) {
compile_sdk_version_codename = *str->value;
@@ -912,7 +912,7 @@ class Linker {
return true;
}
- Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
+ std::optional<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
TRACE_CALL();
// Make sure the first element is <manifest> with package attribute.
xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get());
@@ -937,7 +937,7 @@ class Linker {
if (xml::Attribute* version_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
- Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
+ std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
<< "invalid android:versionCode '" << version_code_attr->value << "'");
@@ -948,7 +948,7 @@ class Linker {
if (xml::Attribute* version_code_major_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
- Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
+ std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
<< "invalid android:versionCodeMajor '"
@@ -960,7 +960,7 @@ class Linker {
if (xml::Attribute* revision_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
- Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
+ std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
<< "invalid android:revisionCode '" << revision_code_attr->value << "'");
@@ -1094,7 +1094,7 @@ class Linker {
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 std::optional<std::string>& out_text_symbols_path = {}) {
if (!options_.generate_java_class_path && !out_text_symbols_path) {
return true;
}
@@ -1251,7 +1251,7 @@ class Linker {
}
const std::string package_utf8 =
- options_.custom_java_package.value_or_default(context_->GetCompilationPackage());
+ options_.custom_java_package.value_or(context_->GetCompilationPackage());
std::string out_path = options_.generate_java_class_path.value();
file::AppendPath(&out_path, file::PackageToPath(package_utf8));
@@ -1283,7 +1283,7 @@ class Linker {
return true;
}
- bool WriteProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keep_set) {
+ bool WriteProguardFile(const std::optional<std::string>& out, const proguard::KeepSet& keep_set) {
TRACE_CALL();
if (!out) {
return true;
@@ -1374,7 +1374,7 @@ class Linker {
res_name.package = context_->GetCompilationPackage();
}
- Maybe<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
+ std::optional<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
if (mangled_name) {
res_name = mangled_name.value();
}
@@ -1550,7 +1550,7 @@ class Linker {
bool CopyAssetsDirsToApk(IArchiveWriter* writer) {
std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets;
for (const std::string& assets_dir : options_.assets_dirs) {
- Maybe<std::vector<std::string>> files =
+ std::optional<std::vector<std::string>> files =
file::FindFiles(assets_dir, context_->GetDiagnostics(), nullptr);
if (!files) {
return false;
@@ -1783,7 +1783,7 @@ class Linker {
package_to_rewrite = table->packages.back().get();
std::string new_package_name =
StringPrintf("%s.%s", package_to_rewrite->name.c_str(),
- app_info_.split_name.value_or_default("feature").c_str());
+ app_info_.split_name.value_or("feature").c_str());
if (context_->IsVerbose()) {
context_->GetDiagnostics()->Note(
@@ -1823,7 +1823,7 @@ class Linker {
}
// First extract the Package name without modifying it (via --rename-manifest-package).
- if (Maybe<AppInfo> maybe_app_info =
+ if (std::optional<AppInfo> maybe_app_info =
ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
const AppInfo& app_info = maybe_app_info.value();
context_->SetCompilationPackage(app_info.package);
@@ -1850,14 +1850,14 @@ class Linker {
return 1;
}
- Maybe<AppInfo> maybe_app_info =
+ std::optional<AppInfo> maybe_app_info =
ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics());
if (!maybe_app_info) {
return 1;
}
app_info_ = maybe_app_info.value();
- context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0));
+ context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or(0));
context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
context_->SetSplitNameDependencies(app_info_.split_name_dependencies);
@@ -2231,7 +2231,7 @@ class Linker {
std::map<size_t, std::string> shared_libs_;
// The package name of the base application, if it is included.
- Maybe<std::string> included_feature_base_;
+ std::optional<std::string> included_feature_base_;
};
int LinkCommand::Action(const std::vector<std::string>& args) {
@@ -2315,7 +2315,8 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
return 1;
}
- const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id_.value());
+ const std::optional<uint32_t> maybe_package_id_int =
+ ResourceUtils::ParseInt(package_id_.value());
if (!maybe_package_id_int) {
context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id_.value()
<< "' is not a valid integer");
@@ -2360,7 +2361,7 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
}
if (preferred_density_) {
- Maybe<uint16_t> density =
+ std::optional<uint16_t> density =
ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics());
if (!density) {
return 1;
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 768b4b2c7bfd..d8c76e297ec5 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -45,21 +45,21 @@ struct LinkOptions {
bool auto_add_overlay = false;
bool override_styles_instead_of_overlaying = false;
OutputFormat output_format = OutputFormat::kApk;
- Maybe<std::string> rename_resources_package;
+ std::optional<std::string> rename_resources_package;
// Java/Proguard options.
- Maybe<std::string> generate_java_class_path;
- Maybe<std::string> custom_java_package;
+ std::optional<std::string> generate_java_class_path;
+ std::optional<std::string> custom_java_package;
std::set<std::string> extra_java_packages;
- Maybe<std::string> generate_text_symbols_path;
- Maybe<std::string> generate_proguard_rules_path;
- Maybe<std::string> generate_main_dex_proguard_rules_path;
+ std::optional<std::string> generate_text_symbols_path;
+ std::optional<std::string> generate_proguard_rules_path;
+ std::optional<std::string> generate_main_dex_proguard_rules_path;
bool generate_conditional_proguard_rules = false;
bool generate_minimal_proguard_rules = false;
bool generate_non_final_ids = false;
bool no_proguard_location_reference = false;
std::vector<std::string> javadoc_annotations;
- Maybe<std::string> private_symbols;
+ std::optional<std::string> private_symbols;
// Optimizations/features.
bool no_auto_version = false;
@@ -70,7 +70,7 @@ struct LinkOptions {
bool no_xml_namespaces = false;
bool do_not_compress_anything = false;
std::unordered_set<std::string> extensions_to_not_compress;
- Maybe<std::regex> regex_to_not_compress;
+ std::optional<std::regex> regex_to_not_compress;
// Static lib options.
bool no_static_lib_packages = false;
@@ -97,7 +97,7 @@ struct LinkOptions {
// Stable ID options.
std::unordered_map<ResourceName, ResourceId> stable_id_map;
- Maybe<std::string> resource_id_map_path;
+ std::optional<std::string> resource_id_map_path;
// When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
// treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
@@ -321,20 +321,20 @@ class LinkCommand : public Command {
std::vector<std::string> overlay_arg_list_;
std::vector<std::string> extra_java_packages_;
- Maybe<std::string> package_id_;
+ std::optional<std::string> package_id_;
std::vector<std::string> configs_;
- Maybe<std::string> preferred_density_;
- Maybe<std::string> product_list_;
- Maybe<std::string> no_compress_regex;
+ std::optional<std::string> preferred_density_;
+ std::optional<std::string> product_list_;
+ std::optional<std::string> no_compress_regex;
bool legacy_x_flag_ = false;
bool require_localization_ = false;
bool verbose_ = false;
bool shared_lib_ = false;
bool static_lib_ = false;
bool proto_format_ = false;
- Maybe<std::string> stable_id_file_path_;
+ std::optional<std::string> stable_id_file_path_;
std::vector<std::string> split_args_;
- Maybe<std::string> trace_folder_;
+ std::optional<std::string> trace_folder_;
};
}// namespace aapt
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 5b18a3789d76..caa3e60d6af1 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -354,7 +354,7 @@ bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
return false;
}
- Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics());
+ auto app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics());
if (!app_info) {
context->GetDiagnostics()->Error(DiagMessage()
<< "failed to extract data from AndroidManifest.xml");
@@ -362,7 +362,7 @@ bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
}
out_options->app_info = std::move(app_info.value());
- context->SetMinSdkVersion(out_options->app_info.min_sdk_version.value_or_default(0));
+ context->SetMinSdkVersion(out_options->app_info.min_sdk_version.value_or(0));
return true;
}
@@ -380,7 +380,7 @@ int OptimizeCommand::Action(const std::vector<std::string>& args) {
if (config_path_) {
std::string& path = config_path_.value();
- Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
+ std::optional<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
if (for_path) {
options_.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
if (!options_.apk_artifacts) {
@@ -427,7 +427,7 @@ int OptimizeCommand::Action(const std::vector<std::string>& args) {
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, diag);
+ std::optional<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
if (!target_density) {
return 1;
}
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 3afc46b04af6..ff63e8dd76d4 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -29,9 +29,9 @@ struct OptimizeOptions {
friend class OptimizeCommand;
// Path to the output APK.
- Maybe<std::string> output_path;
+ std::optional<std::string> output_path;
// Path to the output APK directory for splits.
- Maybe<std::string> output_dir;
+ std::optional<std::string> output_dir;
// Details of the app extracted from the AndroidManifest.xml
AppInfo app_info;
@@ -50,7 +50,7 @@ struct OptimizeOptions {
TableFlattenerOptions table_flattener_options;
- Maybe<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts;
+ std::optional<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts;
// Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
// are kept and will be written as output.
@@ -60,7 +60,7 @@ struct OptimizeOptions {
bool shorten_resource_paths = false;
// Path to the output map of original resource paths to shortened paths.
- Maybe<std::string> shortened_paths_map_path;
+ std::optional<std::string> shortened_paths_map_path;
};
class OptimizeCommand : public Command {
@@ -122,9 +122,9 @@ class OptimizeCommand : public Command {
bool WriteObfuscatedPathsMap(const std::map<std::string, std::string> &path_map,
const std::string &file_path);
- Maybe<std::string> config_path_;
- Maybe<std::string> resources_config_path_;
- Maybe<std::string> target_densities_;
+ std::optional<std::string> config_path_;
+ std::optional<std::string> resources_config_path_;
+ std::optional<std::string> target_densities_;
std::vector<std::string> configs_;
std::vector<std::string> split_args_;
std::unordered_set<std::string> kept_artifacts_;
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 7214f1a68d2c..3244fb83fa4b 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -21,11 +21,10 @@
#include "android-base/logging.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/Locale.h"
-
#include "ResourceUtils.h"
#include "ValueVisitor.h"
#include "split/TableSplitter.h"
-#include "util/Maybe.h"
+
#include "util/Util.h"
using ::android::ConfigDescription;
@@ -35,7 +34,7 @@ using ::android::base::StringPrintf;
namespace aapt {
-Maybe<uint16_t> ParseTargetDensityParameter(const StringPiece& arg, IDiagnostics* diag) {
+std::optional<uint16_t> ParseTargetDensityParameter(const StringPiece& arg, IDiagnostics* diag) {
ConfigDescription preferred_density_config;
if (!ConfigDescription::Parse(arg, &preferred_density_config)) {
diag->Error(DiagMessage() << "invalid density '" << arg << "' for --preferred-density option");
@@ -245,8 +244,8 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
return doc;
}
-static Maybe<std::string> ExtractCompiledString(const xml::Attribute& attr,
- std::string* out_error) {
+static std::optional<std::string> ExtractCompiledString(const xml::Attribute& attr,
+ std::string* out_error) {
if (attr.compiled_value != nullptr) {
const String* compiled_str = ValueCast<String>(attr.compiled_value.get());
if (compiled_str != nullptr) {
@@ -269,7 +268,8 @@ static Maybe<std::string> ExtractCompiledString(const xml::Attribute& attr,
return {};
}
-static Maybe<uint32_t> ExtractCompiledInt(const xml::Attribute& attr, std::string* out_error) {
+static std::optional<uint32_t> ExtractCompiledInt(const xml::Attribute& attr,
+ std::string* out_error) {
if (attr.compiled_value != nullptr) {
const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr.compiled_value.get());
if (compiled_prim != nullptr) {
@@ -283,7 +283,7 @@ static Maybe<uint32_t> ExtractCompiledInt(const xml::Attribute& attr, std::strin
}
// Fallback to the plain text value if there is one.
- Maybe<uint32_t> integer = ResourceUtils::ParseInt(attr.value);
+ std::optional<uint32_t> integer = ResourceUtils::ParseInt(attr.value);
if (integer) {
return integer;
}
@@ -293,7 +293,7 @@ static Maybe<uint32_t> ExtractCompiledInt(const xml::Attribute& attr, std::strin
return {};
}
-static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) {
+static std::optional<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) {
if (attr.compiled_value != nullptr) {
const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr.compiled_value.get());
if (compiled_prim != nullptr) {
@@ -307,7 +307,7 @@ static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out
const String* compiled_str = ValueCast<String>(attr.compiled_value.get());
if (compiled_str != nullptr) {
- Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value);
+ std::optional<int> sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value);
if (sdk_version) {
return sdk_version;
}
@@ -320,7 +320,7 @@ static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out
}
// Fallback to the plain text value if there is one.
- Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
+ std::optional<int> sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
if (sdk_version) {
return sdk_version;
}
@@ -330,8 +330,8 @@ static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out
return {};
}
-Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
- IDiagnostics* diag) {
+std::optional<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
+ IDiagnostics* diag) {
// Make sure the first element is <manifest> with package attribute.
const xml::Element* manifest_el = xml_res.root.get();
if (manifest_el == nullptr) {
@@ -352,7 +352,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
}
std::string error_msg;
- Maybe<std::string> maybe_package = ExtractCompiledString(*package_attr, &error_msg);
+ std::optional<std::string> maybe_package = ExtractCompiledString(*package_attr, &error_msg);
if (!maybe_package) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid package name: " << error_msg);
@@ -362,7 +362,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
if (const xml::Attribute* version_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
- Maybe<uint32_t> maybe_code = ExtractCompiledInt(*version_code_attr, &error_msg);
+ std::optional<uint32_t> maybe_code = ExtractCompiledInt(*version_code_attr, &error_msg);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid android:versionCode: " << error_msg);
@@ -373,7 +373,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
if (const xml::Attribute* version_code_major_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
- Maybe<uint32_t> maybe_code = ExtractCompiledInt(*version_code_major_attr, &error_msg);
+ std::optional<uint32_t> maybe_code = ExtractCompiledInt(*version_code_major_attr, &error_msg);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid android:versionCodeMajor: " << error_msg);
@@ -384,7 +384,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
if (const xml::Attribute* revision_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
- Maybe<uint32_t> maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg);
+ std::optional<uint32_t> maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid android:revisionCode: " << error_msg);
@@ -394,7 +394,8 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
}
if (const xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) {
- Maybe<std::string> maybe_split_name = ExtractCompiledString(*split_name_attr, &error_msg);
+ std::optional<std::string> maybe_split_name =
+ ExtractCompiledString(*split_name_attr, &error_msg);
if (!maybe_split_name) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid split name: " << error_msg);
@@ -406,7 +407,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
if (const xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
if (const xml::Attribute* min_sdk =
uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
- Maybe<int> maybe_sdk = ExtractSdkVersion(*min_sdk, &error_msg);
+ std::optional<int> maybe_sdk = ExtractSdkVersion(*min_sdk, &error_msg);
if (!maybe_sdk) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(uses_sdk_el->line_number))
<< "invalid android:minSdkVersion: " << error_msg);
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index 2a7c62eef678..1b98eb468700 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -26,14 +26,14 @@
#include "SdkConstants.h"
#include "filter/ConfigFilter.h"
#include "split/TableSplitter.h"
-#include "util/Maybe.h"
#include "xml/XmlDom.h"
namespace aapt {
// Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc).
// Returns Nothing and logs a human friendly error message if the string was not legal.
-Maybe<uint16_t> ParseTargetDensityParameter(const android::StringPiece& arg, IDiagnostics* diag);
+std::optional<uint16_t> ParseTargetDensityParameter(const android::StringPiece& arg,
+ IDiagnostics* diag);
// Parses a string of the form 'path/to/output.apk:<config>[,<config>...]' and fills in
// `out_path` with the path and `out_split` with the set of ConfigDescriptions.
@@ -59,8 +59,8 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
const SplitConstraints& constraints);
// Extracts relevant info from the AndroidManifest.xml.
-Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
- IDiagnostics* diag);
+std::optional<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
+ IDiagnostics* diag);
// Returns a copy of 'name' which conforms to the regex '[a-zA-Z]+[a-zA-Z0-9_]*' by
// replacing nonconforming characters with underscores.
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 339b8af5d536..fa816be43be3 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -275,7 +275,7 @@ bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id,
return false;
}
- auto key = ResourceTypeKey{name.type, id.type_id()};
+ auto key = ResourceTypeKey{name.type.type, id.type_id()};
auto type = types_.find(key);
if (type == types_.end()) {
// The type has not been assigned an id yet. Ensure that the specified id is not being used by
@@ -291,7 +291,7 @@ bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id,
if (!visibility.staged_api) {
// Ensure that non-staged resources can only exist in one type ID.
- auto non_staged_type = non_staged_type_ids_.emplace(name.type, id.type_id());
+ auto non_staged_type = non_staged_type_ids_.emplace(name.type.type, id.type_id());
if (!non_staged_type.second && non_staged_type.first->second != id.type_id()) {
diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
<< " because type already has ID " << std::hex
@@ -316,14 +316,14 @@ std::optional<ResourceId> IdAssignerContext::NextId(const ResourceName& name, ID
CHECK(name.package.empty() || name.package == package_name_);
// Find the type id for non-staged resources of this type.
- auto non_staged_type = non_staged_type_ids_.find(name.type);
+ auto non_staged_type = non_staged_type_ids_.find(name.type.type);
if (non_staged_type == non_staged_type_ids_.end()) {
auto next_type_id = type_id_finder_.NextId();
CHECK(next_type_id.has_value()) << "resource type IDs allocated have exceeded maximum (256)";
- non_staged_type = non_staged_type_ids_.emplace(name.type, *next_type_id).first;
+ non_staged_type = non_staged_type_ids_.emplace(name.type.type, *next_type_id).first;
}
- ResourceTypeKey key{name.type, non_staged_type->second};
+ ResourceTypeKey key{name.type.type, non_staged_type->second};
auto type = types_.find(key);
if (type == types_.end()) {
type = types_.emplace(key, TypeGroup(package_id_, key.id)).first;
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index 663776645990..d3575716ae4f 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -57,18 +57,18 @@ TEST_F(IdAssignerTests, AssignIdsWithReservedIds) {
ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
ASSERT_TRUE(VerifyIds(table.get()));
- Maybe<ResourceTable::SearchResult> maybe_result;
+ std::optional<ResourceTable::SearchResult> maybe_result;
// Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
maybe_result = table->FindResource(test::ParseNameOrDie("android:dimen/two"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01020000), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01020000, maybe_result.value().entry->id);
maybe_result =
table->FindResource(test::ParseNameOrDie("android:integer/three"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01030000), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01030000, maybe_result.value().entry->id);
// Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
// IDs.
@@ -76,17 +76,17 @@ TEST_F(IdAssignerTests, AssignIdsWithReservedIds) {
maybe_result =
table->FindResource(test::ParseNameOrDie("android:string/five"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01050000), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01050000, maybe_result.value().entry->id);
// Expect to fill in the gaps between 0x01040000 and 0x01040006.
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/bar"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01040001), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01040001, maybe_result.value().entry->id);
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/baz"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01040002), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01040002, maybe_result.value().entry->id);
}
TEST_F(IdAssignerTests, FailWhenNonUniqueIdsAssigned) {
@@ -143,7 +143,7 @@ TEST_F(IdAssignerTests, AssignIdsWithIdMap) {
ASSERT_TRUE(result);
const ResourceTable::SearchResult& search_result = result.value();
- EXPECT_EQ(make_value<ResourceId>(0x01010002), search_result.entry->id);
+ EXPECT_EQ(0x01010002, search_result.entry->id);
}
TEST_F(IdAssignerTests, UseAllEntryIds) {
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index 79b0933a089f..de1c3bb3dd7e 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -56,7 +56,7 @@ class Visitor : public xml::PackageAwareVisitor {
return;
}
- Maybe<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
+ std::optional<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
if (!ref) {
context_->GetDiagnostics()->Error(DiagMessage(src) << "invalid XML attribute '" << attr->value
<< "'");
@@ -65,7 +65,7 @@ class Visitor : public xml::PackageAwareVisitor {
}
const ResourceName& name = ref.value().name.value();
- Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
+ std::optional<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
if (!maybe_pkg) {
context_->GetDiagnostics()->Error(DiagMessage(src)
<< "invalid namespace prefix '" << name.package << "'");
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 3f574ee8e897..2461438c49b6 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -33,7 +33,7 @@ namespace aapt {
// The struct that represents both Span objects and UntranslatableSections.
struct UnifiedSpan {
// Only present for Span objects. If not present, this was an UntranslatableSection.
- Maybe<std::string> tag;
+ std::optional<std::string> tag;
// The UTF-16 index into the string where this span starts.
uint32_t first_char;
diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp
index 50541152f802..bb72159f9e77 100644
--- a/tools/aapt2/compile/XmlIdCollector.cpp
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -46,7 +46,7 @@ struct IdCollector : public xml::Visitor {
ResourceNameRef name;
bool create = false;
if (ResourceUtils::ParseReference(attr.value, &name, &create, nullptr)) {
- if (create && name.type == ResourceType::kId) {
+ if (create && name.type.type == ResourceType::kId) {
if (!text::IsValidResourceEntryName(name.entry)) {
source_diag_->Error(DiagMessage(element->line_number)
<< "id '" << name << "' has an invalid entry name");
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index dd06b38f6c01..e7a45851e239 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -34,7 +34,6 @@
#include "io/FileSystem.h"
#include "io/StringStream.h"
#include "util/Files.h"
-#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
@@ -113,7 +112,7 @@ std::string GetLabel(const Element* element, IDiagnostics* diag) {
}
/** Returns the value of the version-code-order attribute for a given element. */
-Maybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
+std::optional<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
const xml::Attribute* version = element->FindAttribute("", "version-code-order");
if (version == nullptr) {
std::string label = GetLabel(element, diag);
@@ -135,7 +134,7 @@ class NamespaceVisitor : public xml::Visitor {
/** Copies the values referenced in a configuration group to the target list. */
template <typename T>
-bool CopyXmlReferences(const Maybe<std::string>& name, const Group<T>& groups,
+bool CopyXmlReferences(const std::optional<std::string>& name, const Group<T>& groups,
std::vector<T>* target) {
// If there was no item configured, there is nothing to do and no error.
if (!name) {
@@ -159,7 +158,7 @@ bool CopyXmlReferences(const Maybe<std::string>& name, const Group<T>& groups,
* success, or false if the either the placeholder is not found in the name, or the value is not
* present and the placeholder was.
*/
-bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
+bool ReplacePlaceholder(const StringPiece& placeholder, const std::optional<StringPiece>& value,
std::string* name, IDiagnostics* diag) {
size_t offset = name->find(placeholder.data());
bool found = (offset != std::string::npos);
@@ -207,17 +206,17 @@ xml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfigu
}
/** Converts a ConfiguredArtifact into an OutputArtifact. */
-Maybe<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
- const std::string& apk_name,
- const PostProcessingConfiguration& config,
- IDiagnostics* diag) {
+std::optional<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
+ const std::string& apk_name,
+ const PostProcessingConfiguration& config,
+ IDiagnostics* diag) {
if (!artifact.name && !config.artifact_format) {
diag->Error(
DiagMessage() << "Artifact does not have a name and no global name template defined");
return {};
}
- Maybe<std::string> artifact_name =
+ std::optional<std::string> artifact_name =
(artifact.name) ? artifact.Name(apk_name, diag)
: artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
@@ -287,9 +286,9 @@ Maybe<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
namespace configuration {
/** Returns the binary reprasentation of the XML configuration. */
-Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
- const std::string& config_path,
- IDiagnostics* diag) {
+std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
+ const std::string& config_path,
+ IDiagnostics* diag) {
StringInputStream in(contents);
std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, Source(config_path));
if (!doc) {
@@ -351,7 +350,8 @@ const StringPiece& AbiToString(Abi abi) {
/**
* Returns the common artifact base name from a template string.
*/
-Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
+std::optional<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 =
@@ -359,8 +359,8 @@ Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, I
// 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};
+ auto maybe_base_name = base_name.empty() ? std::nullopt
+ : std::optional<StringPiece>{base_name};
if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
return {};
}
@@ -383,10 +383,10 @@ Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, I
return result;
}
-Maybe<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
- const StringPiece& apk_name,
- IDiagnostics* diag) const {
- Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
+std::optional<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
+ const StringPiece& apk_name,
+ IDiagnostics* diag) const {
+ std::optional<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
if (!base) {
return {};
}
@@ -419,7 +419,8 @@ Maybe<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
return result;
}
-Maybe<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
+std::optional<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name,
+ IDiagnostics* diag) const {
if (!name) {
return {};
}
@@ -430,7 +431,7 @@ Maybe<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name, IDiagno
} // namespace configuration
/** Returns a ConfigurationParser for the file located at the provided path. */
-Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
+std::optional<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
std::string contents;
if (!ReadFileToString(path, &contents, true)) {
return {};
@@ -442,9 +443,9 @@ ConfigurationParser::ConfigurationParser(std::string contents, const std::string
: contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
}
-Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
+std::optional<std::vector<OutputArtifact>> ConfigurationParser::Parse(
const android::StringPiece& apk_path) {
- Maybe<PostProcessingConfiguration> maybe_config =
+ std::optional<PostProcessingConfiguration> maybe_config =
ExtractConfiguration(contents_, config_path_, diag_);
if (!maybe_config) {
return {};
@@ -460,7 +461,8 @@ Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
int version = 1;
for (const ConfiguredArtifact& artifact : config.artifacts) {
- Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
+ std::optional<OutputArtifact> output_artifact =
+ ToOutputArtifact(artifact, apk_name, config, diag_);
if (!output_artifact) {
// Defer return an error condition so that all errors are reported.
valid = false;
@@ -538,7 +540,7 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme
bool valid = true;
OrderedEntry<Abi>& entry = config->abi_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
@@ -589,7 +591,7 @@ bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element*
bool valid = true;
OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
@@ -656,7 +658,7 @@ bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_el
bool valid = true;
OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
@@ -724,19 +726,19 @@ bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_ele
entry.label = attr.value;
valid_attr = true;
} else if (attr.name == "minSdkVersion") {
- Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+ std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
if (version) {
valid_attr = true;
entry.min_sdk_version = version.value();
}
} else if (attr.name == "targetSdkVersion") {
- Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+ std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
if (version) {
valid_attr = true;
entry.target_sdk_version = version;
}
} else if (attr.name == "maxSdkVersion") {
- Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+ std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
if (version) {
valid_attr = true;
entry.max_sdk_version = version;
@@ -778,7 +780,7 @@ bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root
bool valid = true;
OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
@@ -828,7 +830,7 @@ bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element*
bool valid = true;
OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index b9e3be9393c9..195b4baac319 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 <optional>
#include <set>
#include <string>
#include <unordered_map>
@@ -25,7 +26,6 @@
#include "androidfw/ConfigDescription.h"
#include "Diagnostics.h"
-#include "util/Maybe.h"
namespace aapt {
@@ -55,9 +55,9 @@ const android::StringPiece& AbiToString(Abi abi);
*/
struct Locale {
/** The ISO<?> standard locale language code. */
- Maybe<std::string> lang;
+ std::optional<std::string> lang;
/** The ISO<?> standard locale region code. */
- Maybe<std::string> region;
+ std::optional<std::string> region;
inline friend bool operator==(const Locale& lhs, const Locale& rhs) {
return lhs.lang == rhs.lang && lhs.region == rhs.region;
@@ -74,9 +74,9 @@ struct AndroidManifest {
struct AndroidSdk {
std::string label;
int min_sdk_version; // min_sdk_version is mandatory if splitting by SDK.
- Maybe<int> target_sdk_version;
- Maybe<int> max_sdk_version;
- Maybe<AndroidManifest> manifest;
+ std::optional<int> target_sdk_version;
+ std::optional<int> max_sdk_version;
+ std::optional<AndroidManifest> manifest;
static AndroidSdk ForMinSdk(int min_sdk) {
AndroidSdk sdk;
@@ -112,7 +112,7 @@ struct OutputArtifact {
std::vector<Abi> abis;
std::vector<android::ConfigDescription> screen_densities;
std::vector<android::ConfigDescription> locales;
- Maybe<AndroidSdk> android_sdk;
+ std::optional<AndroidSdk> android_sdk;
std::vector<DeviceFeature> features;
std::vector<GlTexture> textures;
@@ -136,7 +136,7 @@ class ConfigurationParser {
public:
/** Returns a ConfigurationParser for the file located at the provided path. */
- static Maybe<ConfigurationParser> ForPath(const std::string& path);
+ static std::optional<ConfigurationParser> ForPath(const std::string& path);
/** Returns a ConfigurationParser for the configuration in the provided file contents. */
static ConfigurationParser ForContents(const std::string& contents, const std::string& path) {
@@ -154,7 +154,8 @@ class ConfigurationParser {
* Parses the configuration file and returns the results. If the configuration could not be parsed
* the result is empty and any errors will be displayed with the provided diagnostics context.
*/
- Maybe<std::vector<configuration::OutputArtifact>> Parse(const android::StringPiece& apk_path);
+ std::optional<std::vector<configuration::OutputArtifact>> Parse(
+ const android::StringPiece& apk_path);
protected:
/**
diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h
index c541688bc018..42ef51591d4f 100644
--- a/tools/aapt2/configuration/ConfigurationParser.internal.h
+++ b/tools/aapt2/configuration/ConfigurationParser.internal.h
@@ -84,8 +84,8 @@ class ComparisonChain {
* have not been able to determine the sort order with the previous comparisons.
*/
template <typename T>
- ComparisonChain& Add(const Group<T>& groups, const Maybe<std::string>& lhs,
- const Maybe<std::string>& rhs) {
+ ComparisonChain& Add(const Group<T>& groups, const std::optional<std::string>& lhs,
+ const std::optional<std::string>& rhs) {
return Add(GetGroupOrder(groups, lhs), GetGroupOrder(groups, rhs));
}
@@ -108,7 +108,7 @@ class ComparisonChain {
private:
template <typename T>
- inline size_t GetGroupOrder(const Entry<T>& groups, const Maybe<std::string>& label) {
+ inline size_t GetGroupOrder(const Entry<T>& groups, const std::optional<std::string>& label) {
if (!label) {
return std::numeric_limits<size_t>::max();
}
@@ -122,32 +122,33 @@ class ComparisonChain {
/** Output artifact configuration options. */
struct ConfiguredArtifact {
/** Name to use for output of processing foo.apk -> foo.<name>.apk. */
- Maybe<std::string> name;
+ std::optional<std::string> name;
/** If present, uses the ABI group with this name. */
- Maybe<std::string> abi_group;
+ std::optional<std::string> abi_group;
/** If present, uses the screen density group with this name. */
- Maybe<std::string> screen_density_group;
+ std::optional<std::string> screen_density_group;
/** If present, uses the locale group with this name. */
- Maybe<std::string> locale_group;
+ std::optional<std::string> locale_group;
/** If present, uses the Android SDK with this name. */
- Maybe<std::string> android_sdk;
+ std::optional<std::string> android_sdk;
/** If present, uses the device feature group with this name. */
- Maybe<std::string> device_feature_group;
+ std::optional<std::string> device_feature_group;
/** If present, uses the OpenGL texture group with this name. */
- Maybe<std::string> gl_texture_group;
+ std::optional<std::string> gl_texture_group;
/** Convert an artifact name template into a name string based on configuration contents. */
- Maybe<std::string> ToArtifactName(const android::StringPiece& format,
- const android::StringPiece& apk_name, IDiagnostics* diag) const;
+ std::optional<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;
+ std::optional<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
};
/** AAPT2 XML configuration file binary representation. */
struct PostProcessingConfiguration {
std::vector<ConfiguredArtifact> artifacts;
- Maybe<std::string> artifact_format;
+ std::optional<std::string> artifact_format;
Group<Abi> abi_groups;
Group<android::ConfigDescription> screen_density_groups;
@@ -212,9 +213,9 @@ struct PostProcessingConfiguration {
};
/** Parses the provided XML document returning the post processing configuration. */
-Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
- const std::string& config_path,
- IDiagnostics* diag);
+std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
+ const std::string& config_path,
+ IDiagnostics* diag);
namespace handler {
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index e5b3107877cb..e5eacccb682e 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -31,11 +31,6 @@ using ::android::ConfigDescription;
namespace aapt {
namespace configuration {
-void PrintTo(const AndroidSdk& sdk, std::ostream* os) {
- *os << "SDK: min=" << sdk.min_sdk_version
- << ", target=" << sdk.target_sdk_version.value_or_default(-1)
- << ", max=" << sdk.max_sdk_version.value_or_default(-1);
-}
bool operator==(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
return lhs.name == rhs.name && lhs.abi_group == rhs.abi_group &&
@@ -45,20 +40,6 @@ bool operator==(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
lhs.gl_texture_group == rhs.gl_texture_group;
}
-std::ostream& operator<<(std::ostream& out, const Maybe<std::string>& value) {
- PrintTo(value, &out);
- return out;
-}
-
-void PrintTo(const ConfiguredArtifact& artifact, std::ostream* os) {
- *os << "\n{"
- << "\n name: " << artifact.name << "\n sdk: " << artifact.android_sdk
- << "\n abi: " << artifact.abi_group << "\n density: " << artifact.screen_density_group
- << "\n locale: " << artifact.locale_group
- << "\n features: " << artifact.device_feature_group
- << "\n textures: " << artifact.gl_texture_group << "\n}\n";
-}
-
namespace handler {
namespace {
@@ -186,7 +167,7 @@ TEST_F(ConfigurationParserTest, ForPath_NoFile) {
}
TEST_F(ConfigurationParserTest, ExtractConfiguration) {
- Maybe<PostProcessingConfiguration> maybe_config =
+ std::optional<PostProcessingConfiguration> maybe_config =
ExtractConfiguration(kValidConfig, "fake.xml", &diag_);
PostProcessingConfiguration config = maybe_config.value();
@@ -928,7 +909,8 @@ TEST(ArtifactTest, Nesting) {
EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", "", &diag));
- const Maybe<std::string>& name = x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
+ const std::optional<std::string>& name =
+ x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
ASSERT_TRUE(name);
EXPECT_EQ(name.value(), "something.${abix86}.apk");
}
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 145d7f873ccd..9828b97982ed 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -92,7 +92,6 @@ enum {
};
const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
-constexpr int kCurrentDevelopmentVersion = 10000;
constexpr int kNeverForLocation = 0x00010000;
/** Retrieves the attribute of the element with the specified attribute resource id. */
@@ -331,7 +330,7 @@ class ManifestExtractor {
ConfigDescription config;
config.orientation = android::ResTable_config::ORIENTATION_PORT;
config.density = android::ResTable_config::DENSITY_MEDIUM;
- config.sdkVersion = kCurrentDevelopmentVersion; // Very high.
+ config.sdkVersion = SDK_CUR_DEVELOPMENT; // Very high.
config.screenWidthDp = 320;
config.screenHeightDp = 480;
config.smallestScreenWidthDp = 320;
@@ -621,7 +620,7 @@ class UsesSdkBadging : public ManifestExtractor::Element {
// Detect the target sdk of the element
if ((min_sdk_name && *min_sdk_name == "Donut")
|| (target_sdk_name && *target_sdk_name == "Donut")) {
- extractor()->RaiseTargetSdk(4);
+ extractor()->RaiseTargetSdk(SDK_DONUT);
}
if (min_sdk) {
extractor()->RaiseTargetSdk(*min_sdk);
@@ -629,7 +628,7 @@ class UsesSdkBadging : public ManifestExtractor::Element {
if (target_sdk) {
extractor()->RaiseTargetSdk(*target_sdk);
} else if (target_sdk_name) {
- extractor()->RaiseTargetSdk(kCurrentDevelopmentVersion);
+ extractor()->RaiseTargetSdk(SDK_CUR_DEVELOPMENT);
}
}
@@ -746,21 +745,23 @@ class SupportsScreen : public ManifestExtractor::Element {
// the screen size support was introduced, so all default to
// enabled.
if (small_screen_temp > 0) {
- small_screen_temp = target_sdk >= 4 ? -1 : 0;
+ small_screen_temp = target_sdk >= SDK_DONUT ? -1 : 0;
}
if (normal_screen_temp > 0) {
normal_screen_temp = -1;
}
if (large_screen_temp > 0) {
- large_screen_temp = target_sdk >= 4 ? -1 : 0;
+ large_screen_temp = target_sdk >= SDK_DONUT ? -1 : 0;
}
if (xlarge_screen_temp > 0) {
// Introduced in Gingerbread.
- xlarge_screen_temp = target_sdk >= 9 ? -1 : 0;
+ xlarge_screen_temp = target_sdk >= SDK_GINGERBREAD ? -1 : 0;
}
if (any_density_temp > 0) {
- any_density_temp = (target_sdk >= 4 || requires_smallest_width_dp > 0
- || compatible_width_limit_dp > 0) ? -1 : 0;
+ any_density_temp = (target_sdk >= SDK_DONUT || requires_smallest_width_dp > 0 ||
+ compatible_width_limit_dp > 0)
+ ? -1
+ : 0;
}
// Print the formatted screen info
@@ -1460,6 +1461,64 @@ class UsesStaticLibrary : public ManifestExtractor::Element {
}
};
+/** Represents <sdk-library> elements. **/
+class SdkLibrary : public ManifestExtractor::Element {
+ public:
+ SdkLibrary() = default;
+ std::string name;
+ int versionMajor;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ printer->Print(
+ StringPrintf("sdk-library: name='%s' versionMajor='%d'\n", name.data(), versionMajor));
+ }
+};
+
+/** Represents <uses-sdk-library> elements. **/
+class UsesSdkLibrary : public ManifestExtractor::Element {
+ public:
+ UsesSdkLibrary() = default;
+ std::string name;
+ int versionMajor;
+ std::vector<std::string> certDigests;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ AddCertDigest(element);
+ }
+ }
+
+ void AddCertDigest(xml::Element* element) {
+ std::string digest = GetAttributeStringDefault(FindAttribute(element, CERT_DIGEST_ATTR), "");
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ digest.erase(std::remove(digest.begin(), digest.end(), ':'), digest.end());
+ if (!digest.empty()) {
+ certDigests.push_back(digest);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ printer->Print(
+ StringPrintf("uses-sdk-library: name='%s' versionMajor='%d'", name.data(), versionMajor));
+ for (size_t i = 0; i < certDigests.size(); i++) {
+ printer->Print(StringPrintf(" certDigest='%s'", certDigests[i].data()));
+ }
+ printer->Print("\n");
+ }
+};
+
/** Represents <uses-native-library> elements. **/
class UsesNativeLibrary : public ManifestExtractor::Element {
public:
@@ -2030,7 +2089,7 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
auto write_external_permission = ElementCast<UsesPermission>(
FindPermission(root.get(), "android.permission.WRITE_EXTERNAL_STORAGE"));
- if (target_sdk() < 4) {
+ if (target_sdk() < SDK_DONUT) {
if (!write_external_permission) {
PrintPermission("android.permission.WRITE_EXTERNAL_STORAGE", "targetSdkVersion < 4", -1);
insert_write_external = true;
@@ -2053,7 +2112,7 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
}
// Pre-JellyBean call log permission compatibility.
- if (target_sdk() < 16) {
+ if (target_sdk() < SDK_JELLY_BEAN) {
if (!FindPermission(root.get(), "android.permission.READ_CALL_LOG")
&& FindPermission(root.get(), "android.permission.READ_CONTACTS")) {
PrintPermission("android.permission.READ_CALL_LOG",
@@ -2366,6 +2425,7 @@ T* ElementCast(ManifestExtractor::Element* element) {
{"required-not-feature", std::is_base_of<RequiredNotFeature, T>::value},
{"screen", std::is_base_of<Screen, T>::value},
{"service", std::is_base_of<Service, T>::value},
+ {"sdk-library", std::is_base_of<SdkLibrary, T>::value},
{"static-library", std::is_base_of<StaticLibrary, T>::value},
{"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
{"supports-input", std::is_base_of<SupportsInput, T>::value},
@@ -2378,6 +2438,7 @@ T* ElementCast(ManifestExtractor::Element* element) {
{"uses-permission", std::is_base_of<UsesPermission, T>::value},
{"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ {"uses-sdk-library", std::is_base_of<UsesSdkLibrary, T>::value},
{"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
};
@@ -2420,6 +2481,7 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
{"required-not-feature", &CreateType<RequiredNotFeature>},
{"screen", &CreateType<Screen>},
{"service", &CreateType<Service>},
+ {"sdk-library", &CreateType<SdkLibrary>},
{"static-library", &CreateType<StaticLibrary>},
{"supports-gl-texture", &CreateType<SupportsGlTexture>},
{"supports-input", &CreateType<SupportsInput>},
@@ -2432,6 +2494,7 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
{"uses-permission", &CreateType<UsesPermission>},
{"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
+ {"uses-sdk-library", &CreateType<UsesSdkLibrary>},
{"uses-static-library", &CreateType<UsesStaticLibrary>},
};
diff --git a/tools/aapt2/format/Archive.cpp b/tools/aapt2/format/Archive.cpp
index 41f01a01ed7c..c20b053c37b1 100644
--- a/tools/aapt2/format/Archive.cpp
+++ b/tools/aapt2/format/Archive.cpp
@@ -43,7 +43,7 @@ class DirectoryWriter : public IArchiveWriter {
bool Open(const StringPiece& out_dir) {
dir_ = out_dir.to_string();
file::FileType type = file::GetFileType(dir_);
- if (type == file::FileType::kNonexistant) {
+ if (type == file::FileType::kNonExistant) {
error_ = "directory does not exist";
return false;
} else if (type != file::FileType::kDirectory) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index a8845ef96bc3..c65c55024bab 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -556,8 +556,8 @@ bool BinaryResourceParser::ParseStagedAliases(const ResChunk_header* chunk) {
std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
const ConfigDescription& config,
const android::Res_value& value) {
- std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(name.type, config, value_pool_,
- value, &table_->string_pool);
+ std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(
+ name.type.type, config, value_pool_, value, &table_->string_pool);
if (files_ != nullptr) {
FileReference* file_ref = ValueCast<FileReference>(item.get());
if (file_ref != nullptr) {
@@ -575,8 +575,10 @@ std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& na
std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef& name,
const ConfigDescription& config,
const ResTable_map_entry* map) {
- switch (name.type) {
+ switch (name.type.type) {
case ResourceType::kStyle:
+ // fallthrough
+ case ResourceType::kConfigVarying: // legacy thing used in tests
return ParseStyle(name, config, map);
case ResourceType::kAttrPrivate:
// fallthrough
@@ -592,8 +594,8 @@ std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef
// We can ignore the value here.
return util::make_unique<Id>();
default:
- diag_->Error(DiagMessage() << "illegal map type '" << to_string(name.type) << "' ("
- << (int)name.type << ")");
+ diag_->Error(DiagMessage() << "illegal map type '" << name.type << "' ("
+ << (int)name.type.type << ")");
break;
}
return {};
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 8139d7385092..cd1c0af702cf 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -123,7 +123,7 @@ class TableFlattenerTest : public ::testing::Test {
return ::testing::AssertionFailure() << "failed to find resource name";
}
- Maybe<ResourceName> resName = ResourceUtils::ToResourceName(actual_name);
+ std::optional<ResourceName> resName = ResourceUtils::ToResourceName(actual_name);
if (!resName) {
return ::testing::AssertionFailure()
<< "expected name '" << expected_res_name << "' but got '"
@@ -423,7 +423,7 @@ TEST_F(TableFlattenerTest, FlattenSharedLibrary) {
ResourceTable result;
ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
- Maybe<ResourceTable::SearchResult> search_result =
+ std::optional<ResourceTable::SearchResult> search_result =
result.FindResource(test::ParseNameOrDie("lib:id/foo"));
ASSERT_TRUE(search_result);
EXPECT_EQ(0x00u, search_result.value().entry->id.value().package_id());
@@ -454,7 +454,7 @@ TEST_F(TableFlattenerTest, FlattenSharedLibraryWithStyle) {
ResourceTable result;
ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
- Maybe<ResourceTable::SearchResult> search_result =
+ std::optional<ResourceTable::SearchResult> search_result =
result.FindResource(test::ParseNameOrDie("lib:style/Theme"));
ASSERT_TRUE(search_result);
EXPECT_EQ(0x00030001u, search_result.value().entry->id.value());
diff --git a/tools/aapt2/format/binary/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
index afbaae4ee2a8..cdbe8828b29b 100644
--- a/tools/aapt2/format/binary/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -264,7 +264,7 @@ class XmlFlattenerVisitor : public xml::ConstVisitor {
}
std::string processed_str;
- Maybe<StringPiece> compiled_text;
+ std::optional<StringPiece> compiled_text;
if (xml_attr->compiled_value != nullptr) {
// Make sure we're not flattening a String. A String can be referencing a string from
// a different StringPool than we're using here to build the binary XML.
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 6042ba89bf8a..f3b7f758e170 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -438,7 +438,7 @@ static pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
}
static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
- pb_ref->set_id(ref.id.value_or_default(ResourceId(0x0)).id);
+ pb_ref->set_id(ref.id.value_or(ResourceId(0x0)).id);
if (ref.name) {
pb_ref->set_name(ref.name.value().to_string());
@@ -759,13 +759,13 @@ void SerializeXmlToPb(const xml::Element& el, pb::XmlNode* out_node,
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({});
+ const ResourceId attr_id = attr.compiled_attribute.value().id.value_or(ResourceId{});
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));
+ pb_src->set_line_number(attr.compiled_value->GetSource().line.value_or(0));
}
}
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 38c811fe3619..d1d72e012b31 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -189,7 +189,7 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
ASSERT_THAT(new_id, NotNull());
EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
- Maybe<ResourceTable::SearchResult> result =
+ std::optional<ResourceTable::SearchResult> result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
ASSERT_TRUE(result);
@@ -234,7 +234,7 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
- Maybe<ResourceTable::SearchResult> search_result =
+ std::optional<ResourceTable::SearchResult> search_result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
@@ -637,7 +637,7 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
EXPECT_THAT(error, IsEmpty());
- Maybe<ResourceTable::SearchResult> search_result =
+ std::optional<ResourceTable::SearchResult> search_result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index e15f935cad27..fc2e45e74b4d 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -21,11 +21,10 @@
#include "android-base/errors.h"
#include "androidfw/StringPiece.h"
#include "utils/FileMap.h"
-
#include "Source.h"
#include "io/FileStream.h"
#include "util/Files.h"
-#include "util/Maybe.h"
+
#include "util/Util.h"
using ::android::StringPiece;
@@ -38,7 +37,7 @@ RegularFile::RegularFile(const Source& source) : source_(source) {}
std::unique_ptr<IData> RegularFile::OpenAsData() {
android::FileMap map;
- if (Maybe<android::FileMap> map = file::MmapPath(source_.path, nullptr)) {
+ if (std::optional<android::FileMap> map = file::MmapPath(source_.path, nullptr)) {
if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
return util::make_unique<MmappedData>(std::move(map.value()));
}
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index de6524dc7027..a963d9893f2f 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -204,7 +204,7 @@ bool JavaClassGenerator::SkipSymbol(Visibility::Level level) {
}
// Whether or not to skip writing this symbol.
-bool JavaClassGenerator::SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol) {
+bool JavaClassGenerator::SkipSymbol(const std::optional<SymbolTable::Symbol>& symbol) {
return !symbol || (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
!symbol.value().is_public);
}
@@ -212,12 +212,12 @@ bool JavaClassGenerator::SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol) {
struct StyleableAttr {
const Reference* attr_ref = nullptr;
std::string field_name;
- Maybe<SymbolTable::Symbol> symbol;
+ std::optional<SymbolTable::Symbol> symbol;
};
static bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
- const ResourceId lhs_id = lhs.attr_ref->id.value_or_default(ResourceId(0));
- const ResourceId rhs_id = rhs.attr_ref->id.value_or_default(ResourceId(0));
+ const ResourceId lhs_id = lhs.attr_ref->id.value_or(ResourceId(0));
+ const ResourceId rhs_id = rhs.attr_ref->id.value_or(ResourceId(0));
if (lhs_id == rhs_id) {
return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
}
@@ -229,8 +229,8 @@ static FieldReference GetRFieldReference(const ResourceName& name,
const std::string package_name =
name.package.empty() ? fallback_package_name.to_string() : name.package;
const std::string entry = JavaClassGenerator::TransformToFieldName(name.entry);
- return FieldReference(
- StringPrintf("%s.R.%s.%s", package_name.c_str(), to_string(name.type).data(), entry.c_str()));
+ return FieldReference(StringPrintf("%s.R.%s.%s", package_name.c_str(),
+ name.type.to_string().data(), entry.c_str()));
}
bool JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
@@ -362,7 +362,7 @@ bool JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
array_def->AddElement(field_name);
r_txt_contents = field_name.ref;
} else {
- const ResourceId attr_id = attr.attr_ref->id.value_or_default(ResourceId(0));
+ const ResourceId attr_id = attr.attr_ref->id.value_or(ResourceId(0));
array_def->AddElement(attr_id);
r_txt_contents = to_string(attr_id);
}
@@ -451,7 +451,7 @@ void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const Reso
MethodDefinition* out_rewrite_method,
text::Printer* r_txt_printer) {
ResourceId real_id = id;
- if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
+ if (context_->GetMinSdkVersion() < SDK_O && name.type.type == ResourceType::kId &&
id.package_id() > kAppPackageId) {
// Workaround for feature splits using package IDs > 0x7F.
// See b/37498913.
@@ -489,7 +489,7 @@ void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const Reso
if (r_txt_printer != nullptr) {
r_txt_printer->Print("int ")
- .Print(to_string(name.type))
+ .Print(name.type.to_string())
.Print(" ")
.Print(field_name)
.Print(" ")
@@ -497,16 +497,16 @@ void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const Reso
}
if (out_rewrite_method != nullptr) {
- const StringPiece& type_str = to_string(name.type);
+ const std::string type_str = name.type.to_string();
out_rewrite_method->AppendStatement(
StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | packageIdBits;", type_str.data(),
field_name.data(), type_str.data(), field_name.data()));
}
}
-Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
- const StringPiece& package_name_to_generate,
- const ResourceEntry& entry) {
+std::optional<std::string> JavaClassGenerator::UnmangleResource(
+ const StringPiece& package_name, const StringPiece& package_name_to_generate,
+ const ResourceEntry& entry) {
if (SkipSymbol(entry.visibility.level)) {
return {};
}
@@ -535,7 +535,7 @@ bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate
MethodDefinition* out_rewrite_method_def,
Printer* r_txt_printer) {
for (const auto& entry : type.entries) {
- const Maybe<std::string> unmangled_name =
+ const std::optional<std::string> unmangled_name =
UnmangleResource(package.name, package_name_to_generate, *entry);
if (!unmangled_name) {
continue;
@@ -561,7 +561,7 @@ bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate
return false;
}
- if (resource_name.type == ResourceType::kStyleable) {
+ if (resource_name.type.type == ResourceType::kStyleable) {
CHECK(!entry->values.empty());
const auto styleable = reinterpret_cast<const Styleable*>(entry->values.front()->value.get());
if (!ProcessStyleable(resource_name, id, *styleable, package_name_to_generate,
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index d9d1b39805f9..b45a2f12db35 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -46,7 +46,7 @@ struct JavaClassGeneratorOptions {
// If set, generates code to rewrite the package ID of resources.
// Implies use_final == true. Default is unset.
- Maybe<OnResourcesLoadedCallbackOptions> rewrite_callback_options;
+ std::optional<OnResourcesLoadedCallbackOptions> rewrite_callback_options;
enum class SymbolTypes {
kAll,
@@ -83,13 +83,13 @@ class JavaClassGenerator {
private:
bool SkipSymbol(Visibility::Level state);
- bool SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol);
+ bool SkipSymbol(const std::optional<SymbolTable::Symbol>& symbol);
// Returns the unmangled resource entry name if the unmangled package is the same as
// package_name_to_generate. Returns nothing if the resource should be skipped.
- Maybe<std::string> UnmangleResource(const android::StringPiece& package_name,
- const android::StringPiece& package_name_to_generate,
- const ResourceEntry& entry);
+ std::optional<std::string> UnmangleResource(const android::StringPiece& package_name,
+ const android::StringPiece& package_name_to_generate,
+ const ResourceEntry& entry);
bool ProcessType(const android::StringPiece& package_name_to_generate,
const ResourceTablePackage& package, const ResourceTableType& type,
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index 09ea03b23c9a..a0db41baecb4 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -19,19 +19,17 @@
#include <algorithm>
#include "Source.h"
-#include "java/AnnotationProcessor.h"
#include "java/ClassDefinition.h"
#include "java/JavaClassGenerator.h"
#include "text/Unicode.h"
-#include "util/Maybe.h"
#include "xml/XmlDom.h"
using ::aapt::text::IsJavaIdentifier;
namespace aapt {
-static Maybe<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
- const std::string& value) {
+static std::optional<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
+ const std::string& value) {
std::string result = value;
size_t pos = value.rfind('.');
if (pos != std::string::npos) {
@@ -63,7 +61,7 @@ static bool WriteSymbol(const Source& source, IDiagnostics* diag, xml::Element*
return false;
}
- Maybe<std::string> result =
+ std::optional<std::string> result =
ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value);
if (!result) {
return false;
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index d9a4caa34e0d..e53e22070b62 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -48,7 +48,7 @@ class BaseVisitor : public xml::Visitor {
void Visit(xml::Element* node) override {
if (!node->namespace_uri.empty()) {
- Maybe<xml::ExtractedPackage> maybe_package =
+ std::optional<xml::ExtractedPackage> maybe_package =
xml::ExtractPackageFromNamespace(node->namespace_uri);
if (maybe_package) {
// This is a custom view, let's figure out the class name from this.
@@ -270,14 +270,16 @@ class ManifestVisitor : public BaseVisitor {
get_name = true;
xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
if (attr) {
- Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ std::optional<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value(), "");
}
}
attr = node->FindAttribute(xml::kSchemaAndroid, "appComponentFactory");
if (attr) {
- Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ std::optional<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value(), "");
}
@@ -285,7 +287,8 @@ class ManifestVisitor : public BaseVisitor {
attr = node->FindAttribute(xml::kSchemaAndroid, "zygotePreloadName");
if (attr) {
- Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ std::optional<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value(), "");
}
@@ -308,7 +311,7 @@ class ManifestVisitor : public BaseVisitor {
component_process ? component_process->value : default_process_;
get_name = !process.empty() && process[0] != ':';
}
- } else if (node->name == "instrumentation") {
+ } else if (node->name == "instrumentation" || node->name == "process") {
get_name = true;
}
@@ -317,7 +320,8 @@ class ManifestVisitor : public BaseVisitor {
get_name = attr != nullptr;
if (get_name) {
- Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ std::optional<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value(), "");
}
@@ -354,7 +358,7 @@ bool CollectProguardRules(IAaptContext* context_, xml::XmlResource* res, KeepSet
return false;
}
- switch (res->file.name.type) {
+ switch (res->file.name.type.type) {
case ResourceType::kLayout: {
LayoutVisitor visitor(res->file, keep_set);
res->root->Accept(&visitor);
@@ -461,7 +465,7 @@ bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
locations->insert(location);
// TODO: allow for more reference types if we can determine its safe.
- if (location.name.type != ResourceType::kLayout) {
+ if (location.name.type.type != ResourceType::kLayout) {
return false;
}
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index e1040666e410..466b7d97da00 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -44,6 +44,9 @@ TEST(ProguardRulesTest, ManifestRuleDefaultConstructorOnly) {
android:name="com.foo.BarApplication"
android:zygotePreloadName="com.foo.BarZygotePreload"
>
+ <processes>
+ <process android:process=":sub" android:name="com.foo.BazApplication" />
+ </processes>
<activity android:name="com.foo.BarActivity"/>
<service android:name="com.foo.BarService"/>
<receiver android:name="com.foo.BarReceiver"/>
@@ -59,6 +62,7 @@ TEST(ProguardRulesTest, ManifestRuleDefaultConstructorOnly) {
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BazApplication { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
diff --git a/tools/aapt2/jni/ScopedUtfChars.h b/tools/aapt2/jni/ScopedUtfChars.h
deleted file mode 100644
index a8c4b136dcf6..000000000000
--- a/tools/aapt2/jni/ScopedUtfChars.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2010 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 SCOPED_UTF_CHARS_H_included
-#define SCOPED_UTF_CHARS_H_included
-
-#include <string.h>
-#include <jni.h>
-
-#include "android-base/logging.h"
-
-// This file was copied with some minor modifications from libnativehelper.
-// As soon as libnativehelper can be compiled for Windows, this file should be
-// replaced with libnativehelper's implementation.
-class ScopedUtfChars {
- public:
- ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
- CHECK(s != nullptr);
- utf_chars_ = env->GetStringUTFChars(s, nullptr);
- }
-
- ScopedUtfChars(ScopedUtfChars&& rhs) :
- env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
- rhs.env_ = nullptr;
- rhs.string_ = nullptr;
- rhs.utf_chars_ = nullptr;
- }
-
- ~ScopedUtfChars() {
- if (utf_chars_) {
- env_->ReleaseStringUTFChars(string_, utf_chars_);
- }
- }
-
- ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
- if (this != &rhs) {
- // Delete the currently owned UTF chars.
- this->~ScopedUtfChars();
-
- // Move the rhs ScopedUtfChars and zero it out.
- env_ = rhs.env_;
- string_ = rhs.string_;
- utf_chars_ = rhs.utf_chars_;
- rhs.env_ = nullptr;
- rhs.string_ = nullptr;
- rhs.utf_chars_ = nullptr;
- }
- return *this;
- }
-
- const char* c_str() const {
- return utf_chars_;
- }
-
- size_t size() const {
- return strlen(utf_chars_);
- }
-
- const char& operator[](size_t n) const {
- return utf_chars_[n];
- }
-
- private:
- JNIEnv* env_;
- jstring string_;
- const char* utf_chars_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
-};
-
-#endif // SCOPED_UTF_CHARS_H_included
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
deleted file mode 100644
index ec3c5431c7a3..000000000000
--- a/tools/aapt2/jni/aapt2_jni.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2015 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 "com_android_tools_aapt2_Aapt2Jni.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "android-base/logging.h"
-#include "ScopedUtfChars.h"
-
-#include "Diagnostics.h"
-#include "cmd/Compile.h"
-#include "cmd/Link.h"
-#include "util/Util.h"
-
-using android::StringPiece;
-
-/*
- * Converts a java List<String> into C++ vector<ScopedUtfChars>.
- */
-static std::vector<ScopedUtfChars> list_to_utfchars(JNIEnv *env, jobject obj) {
- std::vector<ScopedUtfChars> converted;
-
- // Call size() method on the list to know how many elements there are.
- jclass list_cls = env->GetObjectClass(obj);
- jmethodID size_method_id = env->GetMethodID(list_cls, "size", "()I");
- CHECK(size_method_id != 0);
- jint size = env->CallIntMethod(obj, size_method_id);
- CHECK(size >= 0);
-
- // Now, iterate all strings in the list
- // (note: generic erasure means get() return an Object)
- jmethodID get_method_id = env->GetMethodID(list_cls, "get", "(I)Ljava/lang/Object;");
- CHECK(get_method_id != 0);
- for (jint i = 0; i < size; i++) {
- // Call get(i) to get the string in the ith position.
- jobject string_obj_uncast = env->CallObjectMethod(obj, get_method_id, i);
- CHECK(string_obj_uncast != nullptr);
- jstring string_obj = static_cast<jstring>(string_obj_uncast);
- converted.push_back(ScopedUtfChars(env, string_obj));
- }
-
- return converted;
-}
-
-/*
- * Extracts all StringPiece from the ScopedUtfChars instances.
- *
- * The returned pieces can only be used while the original ones have not been
- * destroyed.
- */
-static std::vector<StringPiece> extract_pieces(const std::vector<ScopedUtfChars> &strings) {
- std::vector<StringPiece> pieces;
-
- std::for_each(
- strings.begin(), strings.end(),
- [&pieces](const ScopedUtfChars &p) { pieces.push_back(p.c_str()); });
-
- return pieces;
-}
-
-class JniDiagnostics : public aapt::IDiagnostics {
- public:
- JniDiagnostics(JNIEnv* env, jobject diagnostics_obj)
- : env_(env), diagnostics_obj_(diagnostics_obj) {
- mid_ = NULL;
- }
-
- void Log(Level level, aapt::DiagMessageActual& actual_msg) override {
- jint level_value;
- switch (level) {
- case Level::Error:
- level_value = 3;
- break;
-
- case Level::Warn:
- level_value = 2;
- break;
-
- case Level::Note:
- level_value = 1;
- break;
- }
- jstring message = env_->NewStringUTF(actual_msg.message.c_str());
- jstring path = env_->NewStringUTF(actual_msg.source.path.c_str());
- jlong line = -1;
- if (actual_msg.source.line) {
- line = actual_msg.source.line.value();
- }
- if (!mid_) {
- jclass diagnostics_cls = env_->GetObjectClass(diagnostics_obj_);
- mid_ = env_->GetMethodID(diagnostics_cls, "log", "(ILjava/lang/String;JLjava/lang/String;)V");
- }
- env_->CallVoidMethod(diagnostics_obj_, mid_, level_value, path, line, message);
- }
-
- private:
- JNIEnv* env_;
- jobject diagnostics_obj_;
- jmethodID mid_;
- DISALLOW_COPY_AND_ASSIGN(JniDiagnostics);
-};
-
-JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeCompile(
- JNIEnv* env, jclass aapt_obj, jobject arguments_obj, jobject diagnostics_obj) {
- std::vector<ScopedUtfChars> compile_args_jni =
- list_to_utfchars(env, arguments_obj);
- std::vector<StringPiece> compile_args = extract_pieces(compile_args_jni);
- JniDiagnostics diagnostics(env, diagnostics_obj);
- return aapt::CompileCommand(&diagnostics).Execute(compile_args, &std::cerr);
-}
-
-JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv* env,
- jclass aapt_obj,
- jobject arguments_obj,
- jobject diagnostics_obj) {
- std::vector<ScopedUtfChars> link_args_jni =
- list_to_utfchars(env, arguments_obj);
- std::vector<StringPiece> link_args = extract_pieces(link_args_jni);
- JniDiagnostics diagnostics(env, diagnostics_obj);
- return aapt::LinkCommand(&diagnostics).Execute(link_args, &std::cerr);
-}
-
-JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping(
- JNIEnv *env, jclass aapt_obj) {
- // This is just a no-op method to see if the library has been loaded.
-}
diff --git a/tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h b/tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h
deleted file mode 100644
index 3cd98658fab2..000000000000
--- a/tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class com_android_tools_aapt2_Aapt2Jni */
-
-#ifndef _Included_com_android_tools_aapt2_Aapt2Jni
-#define _Included_com_android_tools_aapt2_Aapt2Jni
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class: com_android_tools_aapt2_Aapt2Jni
- * Method: ping
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping
- (JNIEnv *, jclass);
-
-/*
- * Class: com_android_tools_aapt2_Aapt2Jni
- * Method: nativeCompile
- * Signature: (Ljava/util/List;Lcom/android/tools/aapt2/Aapt2JniDiagnostics;)I
- */
-JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeCompile(JNIEnv*, jclass, jobject,
- jobject);
-
-/*
- * Class: com_android_tools_aapt2_Aapt2Jni
- * Method: nativeLink
- * Signature: (Ljava/util/List;Lcom/android/tools/aapt2/Aapt2JniDiagnostics;)I
- */
-JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv*, jclass, jobject,
- jobject);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 876494e617a6..328ac97090a8 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -90,7 +90,7 @@ bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
}
if (Style* style = ValueCast<Style>(config_value->value.get())) {
- Maybe<ApiVersion> min_sdk_stripped;
+ std::optional<ApiVersion> min_sdk_stripped;
std::vector<Style::Entry> stripped;
auto iter = style->entries.begin();
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 02fd00bba439..8179d46145ca 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -87,25 +87,27 @@ TEST(AutoVersionerTest, VersionStylesForTable) {
Style* style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v4"));
ASSERT_THAT(style, NotNull());
ASSERT_EQ(style->entries.size(), 1u);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")), style->entries.front().key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/onClick"), style->entries.front().key.name);
style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v13"));
ASSERT_THAT(style, NotNull());
ASSERT_EQ(style->entries.size(), 2u);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")),style->entries[0].key.name);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp")), style->entries[1].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/onClick"), style->entries[0].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"),
+ style->entries[1].key.name);
style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v17"));
ASSERT_THAT(style, NotNull());
ASSERT_EQ(style->entries.size(), 3u);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")), style->entries[0].key.name);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp")), style->entries[1].key.name);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/paddingStart")), style->entries[2].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/onClick"), style->entries[0].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"),
+ style->entries[1].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/paddingStart"), style->entries[2].key.name);
style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v21"));
ASSERT_THAT(style, NotNull());
ASSERT_EQ(1u, style->entries.size());
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/paddingEnd")), style->entries.front().key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/paddingEnd"), style->entries.front().key.name);
}
} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 8abd9dec56be..d432341a8cde 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -52,7 +52,7 @@ static bool NameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
// We allow unqualified class names (ie: .HelloActivity)
// Since we don't know the package name, we can just make a fake one here and
// the test will be identical as long as the real package name is valid too.
- Maybe<std::string> fully_qualified_class_name =
+ std::optional<std::string> fully_qualified_class_name =
util::GetFullyQualifiedClassName("a", attr->value);
StringPiece qualified_class_name = fully_qualified_class_name
@@ -146,7 +146,7 @@ static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics*
// Now inject the android:isFeatureSplit="true" attribute.
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsFeatureSplit);
if (attr != nullptr) {
- if (!ResourceUtils::ParseBool(attr->value).value_or_default(false)) {
+ if (!ResourceUtils::ParseBool(attr->value).value_or(false)) {
// The isFeatureSplit attribute is false, which conflicts with the use
// of "featureSplit".
diag->Error(DiagMessage(el->line_number)
@@ -163,6 +163,31 @@ static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics*
return true;
}
+static bool AutoGenerateIsSplitRequired(xml::Element* el, SourcePathDiagnostics* diag) {
+ constexpr const char* kRequiredSplitTypes = "requiredSplitTypes";
+ constexpr const char* kIsSplitRequired = "isSplitRequired";
+
+ xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kRequiredSplitTypes);
+ if (attr != nullptr) {
+ // Now inject the android:isSplitRequired="true" attribute.
+ xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsSplitRequired);
+ if (attr != nullptr) {
+ if (!ResourceUtils::ParseBool(attr->value).value_or(false)) {
+ // The isFeatureSplit attribute is false, which conflicts with the use
+ // of "featureSplit".
+ diag->Error(DiagMessage(el->line_number)
+ << "attribute 'requiredSplitTypes' used in <manifest> but "
+ "'android:isSplitRequired' is not 'true'");
+ return false;
+ }
+ // The attribute is already there and set to true, nothing to do.
+ } else {
+ el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, kIsSplitRequired, "true"});
+ }
+ }
+ return true;
+}
+
static bool VerifyManifest(xml::Element* el, xml::XmlActionExecutorPolicy policy,
SourcePathDiagnostics* diag) {
xml::Attribute* attr = el->FindAttribute({}, "package");
@@ -329,6 +354,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
// Manifest actions.
xml::XmlNodeAction& manifest_action = (*executor)["manifest"];
manifest_action.Action(AutoGenerateIsFeatureSplit);
+ manifest_action.Action(AutoGenerateIsSplitRequired);
manifest_action.Action(VerifyManifest);
manifest_action.Action(FixCoreAppAttribute);
manifest_action.Action([&](xml::Element* el) -> bool {
@@ -482,6 +508,16 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
uses_static_library_action["additional-certificate"];
+ xml::XmlNodeAction& sdk_library_action = application_action["sdk-library"];
+ sdk_library_action.Action(RequiredNameIsJavaPackage);
+ sdk_library_action.Action(RequiredAndroidAttribute("versionMajor"));
+
+ xml::XmlNodeAction& uses_sdk_library_action = application_action["uses-sdk-library"];
+ uses_sdk_library_action.Action(RequiredNameIsJavaPackage);
+ uses_sdk_library_action.Action(RequiredAndroidAttribute("versionMajor"));
+ uses_sdk_library_action.Action(RequiredAndroidAttribute("certDigest"));
+ uses_sdk_library_action["additional-certificate"];
+
xml::XmlNodeAction& uses_package_action = application_action["uses-package"];
uses_package_action.Action(RequiredNameIsJavaPackage);
uses_package_action["additional-certificate"];
@@ -508,6 +544,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
application_action["activity-alias"] = component_action;
application_action["service"] = component_action;
application_action["receiver"] = component_action;
+ application_action["apex-system-service"] = component_action;
// Provider actions.
application_action["provider"] = component_action;
@@ -523,7 +560,8 @@ static void FullyQualifyClassName(const StringPiece& package, const StringPiece&
const StringPiece& attr_name, xml::Element* el) {
xml::Attribute* attr = el->FindAttribute(attr_ns, attr_name);
if (attr != nullptr) {
- if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package, attr->value)) {
+ if (std::optional<std::string> new_value =
+ util::GetFullyQualifiedClassName(package, attr->value)) {
attr->value = std::move(new_value.value());
}
}
@@ -550,10 +588,21 @@ static bool RenameManifestPackage(const StringPiece& package_override, xml::Elem
child_el->name == "provider" || child_el->name == "receiver" ||
child_el->name == "service") {
FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", child_el);
+ continue;
}
if (child_el->name == "activity-alias") {
FullyQualifyClassName(original_package, xml::kSchemaAndroid, "targetActivity", child_el);
+ continue;
+ }
+
+ if (child_el->name == "processes") {
+ for (xml::Element* grand_child_el : child_el->GetChildElements()) {
+ if (grand_child_el->name == "process") {
+ FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", grand_child_el);
+ }
+ }
+ continue;
}
}
}
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 34ad8d586df1..d5d1d1770e1c 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -22,7 +22,7 @@
#include "android-base/macros.h"
#include "process/IResourceTableConsumer.h"
-#include "util/Maybe.h"
+
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
@@ -30,47 +30,47 @@ namespace aapt {
struct ManifestFixerOptions {
// The minimum SDK version to set if no 'android:minSdkVersion' is defined in a <uses-sdk> tag.
- Maybe<std::string> min_sdk_version_default;
+ std::optional<std::string> min_sdk_version_default;
// The target SDK version to set if no 'android:targetSdkVersion' is defined in a <uses-sdk> tag.
- Maybe<std::string> target_sdk_version_default;
+ std::optional<std::string> target_sdk_version_default;
// The Android package to use instead of the one defined in 'package' in <manifest>.
// This also renames all relative package/class names in the manifest to fully qualified
// Java names.
- Maybe<std::string> rename_manifest_package;
+ std::optional<std::string> rename_manifest_package;
// The Android package to use instead of the one defined in 'android:targetPackage' in
// <instrumentation>.
- Maybe<std::string> rename_instrumentation_target_package;
+ std::optional<std::string> rename_instrumentation_target_package;
// The Android package to use instead of the one defined in 'android:targetPackage' in
// <overlay>.
- Maybe<std::string> rename_overlay_target_package;
+ std::optional<std::string> rename_overlay_target_package;
// The version name to set if 'android:versionName' is not defined in <manifest> or if
// replace_version is set.
- Maybe<std::string> version_name_default;
+ std::optional<std::string> version_name_default;
// The version code to set if 'android:versionCode' is not defined in <manifest> or if
// replace_version is set.
- Maybe<std::string> version_code_default;
+ std::optional<std::string> version_code_default;
// The version code to set if 'android:versionCodeMajor' is not defined in <manifest> or if
// replace_version is set.
- Maybe<std::string> version_code_major_default;
+ std::optional<std::string> version_code_major_default;
// The revision code to set if 'android:revisionCode' is not defined in <manifest> or if
// replace_version is set.
- Maybe<std::string> revision_code_default;
+ std::optional<std::string> revision_code_default;
// The version of the framework being compiled against to set for 'android:compileSdkVersion' in
// the <manifest> tag.
- Maybe<std::string> compile_sdk_version;
+ std::optional<std::string> compile_sdk_version;
// The version codename of the framework being compiled against to set for
// 'android:compileSdkVersionCodename' in the <manifest> tag.
- Maybe<std::string> compile_sdk_version_codename;
+ std::optional<std::string> compile_sdk_version_codename;
// Whether validation errors should be treated only as warnings. If this is 'true', then an
// incorrect node will not result in an error, but only as a warning, and the parsing will
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 4ac25bd6a0e0..5372cf243951 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -190,7 +190,8 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
public:
EmptyDeclStack() = default;
- Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+ std::optional<xml::ExtractedPackage> TransformPackageAlias(
+ const StringPiece& alias) const override {
if (alias.empty()) {
return xml::ExtractedPackage{{}, true /*private*/};
}
@@ -206,7 +207,8 @@ struct MacroDeclStack : public xml::IPackageDeclStack {
: alias_namespaces_(std::move(namespaces)) {
}
- Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+ std::optional<xml::ExtractedPackage> TransformPackageAlias(
+ const StringPiece& alias) const override {
if (alias.empty()) {
return xml::ExtractedPackage{{}, true /*private*/};
}
@@ -322,11 +324,11 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
return symbol;
}
-Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
- const CallSite& callsite,
- IAaptContext* context,
- SymbolTable* symbols,
- std::string* out_error) {
+std::optional<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
+ const CallSite& callsite,
+ IAaptContext* context,
+ SymbolTable* symbols,
+ std::string* out_error) {
const SymbolTable::Symbol* symbol =
ResolveAttributeCheckVisibility(reference, callsite, context, symbols, out_error);
if (!symbol) {
@@ -350,7 +352,7 @@ void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& c
}
const ResourceName& ref_name = ref.name.value();
- CHECK_EQ(ref_name.type, ResourceType::kAttr);
+ CHECK_EQ(ref_name.type.type, ResourceType::kAttr);
if (!ref_name.package.empty()) {
*out_msg << ref_name.package << ":";
@@ -383,7 +385,7 @@ std::unique_ptr<Item> ReferenceLinker::LinkReference(const CallSite& callsite,
Reference transformed_reference = reference;
xml::ResolvePackage(decls, &transformed_reference);
- if (transformed_reference.name.value().type == ResourceType::kMacro) {
+ if (transformed_reference.name.value().type.type == ResourceType::kMacro) {
if (transformed_reference.name.value().package.empty()) {
transformed_reference.name.value().package = callsite.package;
}
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 770f1e500ac0..b46085397c52 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -97,11 +97,11 @@ class ReferenceLinker : public IResourceTableConsumer {
// 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,
- IAaptContext* context,
- SymbolTable* symbols,
- std::string* out_error);
+ static std::optional<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
+ const CallSite& callsite,
+ IAaptContext* context,
+ SymbolTable* symbols,
+ std::string* out_error);
// Writes the resource name to the DiagMessage, using the
// "orig_name (aka <transformed_name>)" syntax.
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 2d8f0d39053f..97bdd3ebae66 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -317,12 +317,12 @@ TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) {
CallSite{"com.app.test"},
context.get(), &table);
ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
+ EXPECT_THAT(s->id, Eq(0x7f010000));
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
context.get(), &table);
ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
+ EXPECT_THAT(s->id, Eq(0x7f010001));
EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
CallSite{"com.app.bad"}, context.get(), &table),
@@ -348,7 +348,7 @@ TEST(ReferenceLinkerTest, ReferenceSymbolFromOtherSplit) {
CallSite{"com.app.test"},
context.get(), &table);
ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x80010000)));
+ EXPECT_THAT(s->id, Eq(0x80010000));
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
context.get(), &table);
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 22f4d18dc3ca..d78f0ac17f67 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -303,8 +303,8 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package,
dst_config_value->value = std::move(new_file_ref);
} else {
- Maybe<std::string> original_comment = (dst_config_value->value)
- ? dst_config_value->value->GetComment() : Maybe<std::string>();
+ auto original_comment = (dst_config_value->value)
+ ? dst_config_value->value->GetComment() : std::optional<std::string>();
dst_config_value->value = src_config_value->value->Transform(cloner);
@@ -349,7 +349,7 @@ bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFi
file_ref->file = file;
ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package);
- pkg->FindOrCreateType(file_desc.name.type)
+ pkg->FindOrCreateType(file_desc.name.type.type)
->FindOrCreateEntry(file_desc.name.entry)
->FindOrCreateValue(file_desc.config, {})
->value = std::move(file_ref);
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 4358fb565a7d..4cbf2d3a826c 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -409,8 +409,7 @@ TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) {
const auto expected = ResourceUtils::MakeBool(true);
EXPECT_THAT(style->entries, Contains(Field(&Style::Entry::value, Pointee(ValueEq(*expected)))));
- EXPECT_THAT(style->parent,
- Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")))));
+ EXPECT_THAT(style->parent, Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")));
}
TEST_F(TableMergerTest, OverrideStyleInsteadOfOverlaying) {
@@ -483,7 +482,7 @@ TEST_F(TableMergerTest, SetOverlayable) {
ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
- Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+ std::optional<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
@@ -517,7 +516,7 @@ TEST_F(TableMergerTest, SetOverlayableLater) {
ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
- Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+ std::optional<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index aaa085e2eb15..1f8548b5de75 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -68,7 +68,7 @@ class XmlVisitor : public xml::PackageAwareVisitor {
const Attribute* attribute = &default_attribute;
- if (Maybe<xml::ExtractedPackage> maybe_package =
+ if (std::optional<xml::ExtractedPackage> maybe_package =
xml::ExtractPackageFromNamespace(attr.namespace_uri)) {
// There is a valid package name for this attribute. We will look this up.
Reference attr_ref(
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index ddf5b9a22c2f..6d96cf1cf502 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -100,18 +100,18 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010000)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010000), xml_attr->compiled_attribute.value().id);
EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "background");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010001)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010001), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
- EXPECT_EQ(make_value(test::ParseNameOrDie("color/green")), ref->name); // Make sure the name
- // didn't change.
- EXPECT_EQ(make_value(ResourceId(0x7f020000)), ref->id);
+ EXPECT_EQ(test::ParseNameOrDie("color/green"), ref->name); // Make sure the name
+ // didn't change.
+ EXPECT_EQ(ResourceId(0x7f020000), ref->id);
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
ASSERT_THAT(xml_attr, NotNull());
@@ -172,7 +172,7 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
view_el->FindAttribute(xml::BuildPackageNamespace("com.android.support"), "colorAccent");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x7f010001)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x7f010001), xml_attr->compiled_attribute.value().id);
EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
}
@@ -190,11 +190,11 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x7f010000)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x7f010000), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
ASSERT_TRUE(ref->name);
- EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);
+ EXPECT_EQ(ResourceId(0x7f020001), ref->id);
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
@@ -214,10 +214,10 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010002)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010002), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
- EXPECT_EQ(make_value(ResourceId(0x01030000)), ref->id);
+ EXPECT_EQ(ResourceId(0x01030000), ref->id);
ASSERT_FALSE(view_el->GetChildElements().empty());
view_el = view_el->GetChildElements().front();
@@ -228,10 +228,10 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x7f010002), xml_attr->compiled_attribute.value().id);
ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
- EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
+ EXPECT_EQ(ResourceId(0x7f030000), ref->id);
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
@@ -250,10 +250,10 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
xml::Attribute* xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x7f010002), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
- EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
+ EXPECT_EQ(ResourceId(0x7f030000), ref->id);
}
@@ -270,7 +270,7 @@ TEST_F(XmlReferenceLinkerTest, AddAngleOnGradientForAndroidQ) {
xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010004), xml_attr->compiled_attribute.value().id);
BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
ASSERT_THAT(value, NotNull());
@@ -292,7 +292,7 @@ TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForAndroidQ) {
xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010004), xml_attr->compiled_attribute.value().id);
BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
ASSERT_THAT(value, NotNull());
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index d385267fe5ed..92b45c397eed 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -75,8 +75,8 @@ const SymbolTable::Symbol* SymbolTable::FindByName(const ResourceName& name) {
// Fill in the package name if necessary.
// If there is no package in `name`, we will need to copy the ResourceName
- // and store it somewhere; we use the Maybe<> class to reserve storage.
- Maybe<ResourceName> name_with_package_impl;
+ // and store it somewhere; we use the std::optional<> class to reserve storage.
+ std::optional<ResourceName> name_with_package_impl;
if (name.package.empty()) {
name_with_package_impl = ResourceName(mangler_->GetTargetPackageName(), name.type, name.entry);
name_with_package = &name_with_package_impl.value();
@@ -88,9 +88,9 @@ const SymbolTable::Symbol* SymbolTable::FindByName(const ResourceName& name) {
}
// The name was not found in the cache. Mangle it (if necessary) and find it in our sources.
- // Again, here we use a Maybe<> object to reserve storage if we need to mangle.
+ // Again, here we use a std::optional<> object to reserve storage if we need to mangle.
const ResourceName* mangled_name = name_with_package;
- Maybe<ResourceName> mangled_name_impl;
+ std::optional<ResourceName> mangled_name_impl;
if (mangler_->ShouldMangle(name_with_package->package)) {
mangled_name_impl = mangler_->MangleName(*name_with_package);
mangled_name = &mangled_name_impl.value();
@@ -183,9 +183,9 @@ std::unique_ptr<SymbolTable::Symbol> DefaultSymbolTableDelegate::FindById(
std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::FindByName(
const ResourceName& name) {
- Maybe<ResourceTable::SearchResult> result = table_->FindResource(name);
+ std::optional<ResourceTable::SearchResult> result = table_->FindResource(name);
if (!result) {
- if (name.type == ResourceType::kAttr) {
+ if (name.type.type == ResourceType::kAttr) {
// Recurse and try looking up a private attribute.
return FindByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
}
@@ -203,7 +203,7 @@ std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::FindByName(
(sr.entry->id.value().package_id() == 0) || sr.entry->visibility.staged_api;
}
- if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
+ if (name.type.type == ResourceType::kAttr || name.type.type == ResourceType::kAttrPrivate) {
const ConfigDescription kDefaultConfig;
ResourceConfigValue* config_value = sr.entry->FindValue(kDefaultConfig);
if (config_value) {
@@ -306,7 +306,7 @@ static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
return nullptr;
}
- Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(*name);
+ std::optional<ResourceName> parsed_name = ResourceUtils::ToResourceName(*name);
if (!parsed_name) {
return nullptr;
}
@@ -366,7 +366,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
}
std::unique_ptr<SymbolTable::Symbol> s;
- if (real_name.type == ResourceType::kAttr) {
+ if (real_name.type.type == ResourceType::kAttr) {
s = LookupAttributeInTable(asset_manager_, res_id);
} else {
s = util::make_unique<SymbolTable::Symbol>();
@@ -382,8 +382,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
return {};
}
-static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
- ResourceId id) {
+static std::optional<ResourceName> GetResourceName(android::AssetManager2& am, ResourceId id) {
auto name = am.GetResourceName(id.id);
if (!name.has_value()) {
return {};
@@ -402,7 +401,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
return {};
}
- Maybe<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
+ std::optional<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
if (!maybe_name) {
return {};
}
@@ -414,7 +413,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
ResourceName& name = maybe_name.value();
std::unique_ptr<SymbolTable::Symbol> s;
- if (name.type == ResourceType::kAttr) {
+ if (name.type.type == ResourceType::kAttr) {
s = LookupAttributeInTable(asset_manager_, id);
} else {
s = util::make_unique<SymbolTable::Symbol>();
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 06eaf63ad442..c17837c224ab 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -38,7 +38,7 @@ inline android::hash_t hash_type(const ResourceName& name) {
std::hash<std::string> str_hash;
android::hash_t hash = 0;
hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.package));
- hash = android::JenkinsHashMix(hash, (uint32_t)name.type);
+ hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.type.name));
hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.entry));
return hash;
}
@@ -56,7 +56,7 @@ class SymbolTable {
struct Symbol {
Symbol() = default;
- explicit Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr = {},
+ explicit Symbol(const std::optional<ResourceId>& i, const std::shared_ptr<Attribute>& attr = {},
bool pub = false)
: id(i), attribute(attr), is_public(pub) {
}
@@ -66,7 +66,7 @@ class SymbolTable {
Symbol& operator=(const Symbol&) = default;
Symbol& operator=(Symbol&&) = default;
- Maybe<ResourceId> id;
+ std::optional<ResourceId> id;
std::shared_ptr<Attribute> attribute;
bool is_public = false;
bool is_dynamic = false;
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 4816596da487..23331de02df7 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -159,7 +159,8 @@ std::unique_ptr<ResourceTable> ResourceTableBuilder::Build() {
return std::move(table_);
}
-std::unique_ptr<Reference> BuildReference(const StringPiece& ref, const Maybe<ResourceId>& id) {
+std::unique_ptr<Reference> BuildReference(const StringPiece& ref,
+ const std::optional<ResourceId>& id) {
std::unique_ptr<Reference> reference = util::make_unique<Reference>(ParseNameOrDie(ref));
reference->id = id;
return reference;
@@ -218,7 +219,8 @@ std::unique_ptr<Style> StyleBuilder::Build() {
return std::move(style_);
}
-StyleableBuilder& StyleableBuilder::AddItem(const StringPiece& str, const Maybe<ResourceId>& id) {
+StyleableBuilder& StyleableBuilder::AddItem(const StringPiece& str,
+ const std::optional<ResourceId>& id) {
styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
styleable_->entries.back().id = id;
return *this;
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 3ff955d65f24..55778aea40af 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -29,7 +29,6 @@
#include "configuration/ConfigurationParser.internal.h"
#include "process/IResourceTableConsumer.h"
#include "test/Common.h"
-#include "util/Maybe.h"
#include "xml/XmlDom.h"
namespace aapt {
@@ -86,7 +85,7 @@ class ResourceTableBuilder {
};
std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
- const Maybe<ResourceId>& id = {});
+ const std::optional<ResourceId>& id = {});
std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type, uint32_t data);
template <typename T>
@@ -149,7 +148,8 @@ class StyleBuilder {
class StyleableBuilder {
public:
StyleableBuilder() = default;
- StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {});
+ StyleableBuilder& AddItem(const android::StringPiece& str,
+ const std::optional<ResourceId>& id = {});
std::unique_ptr<Styleable> Build();
private:
diff --git a/tools/aapt2/test/Common.cpp b/tools/aapt2/test/Common.cpp
index 23c22185a53f..e029d025b366 100644
--- a/tools/aapt2/test/Common.cpp
+++ b/tools/aapt2/test/Common.cpp
@@ -48,7 +48,7 @@ Value* GetValueForConfigAndProduct<Value>(ResourceTable* table,
const android::StringPiece& res_name,
const ConfigDescription& config,
const android::StringPiece& product) {
- Maybe<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
+ std::optional<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
if (result) {
ResourceConfigValue* config_value = result.value().entry->FindValue(config, product);
if (config_value) {
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 777ca5c72619..7006964d6f88 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -55,7 +55,7 @@ template <typename T = Value>
T* GetValueForConfigAndProduct(ResourceTable* table, const android::StringPiece& res_name,
const android::ConfigDescription& config,
const android::StringPiece& product) {
- Maybe<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
+ std::optional<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
if (result) {
ResourceConfigValue* config_value = result.value().entry->FindValue(config, product);
if (config_value) {
@@ -130,7 +130,7 @@ template std::ostream& operator<<<Plural>(std::ostream&, const Plural&);
// Add a print method to Maybe.
template <typename T>
-void PrintTo(const Maybe<T>& value, std::ostream* out) {
+void PrintTo(const std::optional<T>& value, std::ostream* out) {
if (value) {
*out << ::testing::PrintToString(value.value());
} else {
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 5d8ded39e654..e1b8dd5687ff 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -95,8 +95,8 @@ class Context : public IAaptContext {
friend class ContextBuilder;
PackageType package_type_ = PackageType::kApp;
- Maybe<std::string> compilation_package_;
- Maybe<uint8_t> package_id_;
+ std::optional<std::string> compilation_package_;
+ std::optional<uint8_t> package_id_;
StdErrDiagnostics diagnostics_;
NameMangler name_mangler_;
SymbolTable symbols_;
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 285e5a11b4c0..ddc1853ca13c 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -67,8 +67,7 @@ void ClearDirectory(const android::StringPiece& path) {
}
void TestDirectoryFixture::SetUp() {
- temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(),
- "_temp",
+ temp_dir_ = file::BuildPath({testing::TempDir(), "_temp",
testing::UnitTest::GetInstance()->current_test_case()->name(),
testing::UnitTest::GetInstance()->current_test_info()->name()});
ASSERT_TRUE(file::mkdirs(temp_dir_));
@@ -126,7 +125,7 @@ bool CommandTestFixture::Link(const std::vector<std::string>& args,
link_args.insert(link_args.end(), {"-I", android_sdk});
// Add the files from the compiled resources directory to the link file arguments
- Maybe<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
+ std::optional<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
if (compiled_files) {
for (std::string& compile_file : compiled_files.value()) {
compile_file = file::BuildPath({flat_dir, compile_file});
@@ -236,4 +235,4 @@ std::vector<std::string> LinkCommandBuilder::Build(const std::string& out_apk) {
return args_;
}
-} // namespace aapt \ No newline at end of file
+} // namespace aapt
diff --git a/tools/aapt2/tools/finalize_res.py b/tools/aapt2/tools/finalize_res.py
new file mode 100755
index 000000000000..0e4d865bc890
--- /dev/null
+++ b/tools/aapt2/tools/finalize_res.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2022 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.
+#
+# 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.
+
+"""
+Finalize resource values in <staging-public-group> tags
+and convert those to <staging-public-group-final>
+
+Usage: $ANDROID_BUILD_TOP/frameworks/base/tools/aapt2/tools/finalize_res.py \
+ $ANDROID_BUILD_TOP/frameworks/base/core/res/res/values/public-staging.xml \
+ $ANDROID_BUILD_TOP/frameworks/base/core/res/res/values/public-final.xml
+"""
+
+import re
+import sys
+
+resTypes = ["attr", "id", "style", "string", "dimen", "color", "array", "drawable", "layout",
+ "anim", "animator", "interpolator", "mipmap", "integer", "transition", "raw", "bool",
+ "fraction"]
+
+_type_ids = {}
+_type = ""
+
+_lowest_staging_first_id = 0x01FFFFFF
+
+"""
+ Created finalized <public> declarations for staging resources, ignoring them if they've been
+ prefixed with removed_. The IDs are assigned without holes starting from the last ID for that
+ type currently finalized in public-final.xml.
+"""
+def finalize_item(raw):
+ name = raw.group(1)
+ if re.match(r'_*removed.+', name):
+ return ""
+ id = _type_ids[_type]
+ _type_ids[_type] += 1
+ return ' <public type="%s" name="%s" id="%s" />\n' % (_type, name, '0x{0:0{1}x}'.format(id, 8))
+
+
+"""
+ Finalizes staging-public-groups if they have any entries in them. Also keeps track of the
+ lowest first-id of the non-empty groups so that the next release's staging-public-groups can
+ be assigned the next down shifted first-id.
+"""
+def finalize_group(raw):
+ global _type, _lowest_staging_first_id
+ _type = raw.group(1)
+ id = int(raw.group(2), 16)
+ _type_ids[_type] = _type_ids.get(_type, id)
+ (res, count) = re.subn(' {0,4}<public name="(.+?)" */>\n', finalize_item, raw.group(3))
+ if count > 0:
+ res = raw.group(0).replace("staging-public-group",
+ "staging-public-group-final") + '\n' + res
+ _lowest_staging_first_id = min(id, _lowest_staging_first_id)
+ return res
+
+"""
+ Collects the max ID for each resType so that the new IDs can be assigned afterwards
+"""
+def collect_ids(raw):
+ for m in re.finditer(r'<public type="(.+?)" name=".+?" id="(.+?)" />', raw):
+ type = m.group(1)
+ id = int(m.group(2), 16)
+ _type_ids[type] = max(id + 1, _type_ids.get(type, 0))
+
+
+with open(sys.argv[1], "r+") as stagingFile:
+ with open(sys.argv[2], "r+") as finalFile:
+ existing = finalFile.read()
+ # Cut out the closing resources tag so that it can be concatenated easily later
+ existing = "\n".join(existing.rsplit("</resources>", 1))
+
+ # Collect the IDs from the existing already finalized resources
+ collect_ids(existing)
+
+ staging = stagingFile.read()
+ stagingSplit = staging.rsplit("<resources>")
+ staging = stagingSplit[1]
+ staging = re.sub(
+ r'<staging-public-group type="(.+?)" first-id="(.+?)">(.+?)</staging-public-group>',
+ finalize_group, staging, flags=re.DOTALL)
+ staging = re.sub(r' *\n', '\n', staging)
+ staging = re.sub(r'\n{3,}', '\n\n', staging)
+
+ # First write the existing finalized declarations and then append the new stuff
+ finalFile.seek(0)
+ finalFile.write(existing.strip("\n"))
+ finalFile.write("\n\n")
+ finalFile.write(staging.strip("\n"))
+ finalFile.write("\n")
+ finalFile.truncate()
+
+ stagingFile.seek(0)
+ # Include the documentation from public-staging.xml that was previously split out
+ stagingFile.write(stagingSplit[0])
+ # Write the next platform header
+ stagingFile.write("<resources>\n\n")
+ stagingFile.write(" <!-- ===============================================================\n")
+ stagingFile.write(" Resources added in version NEXT of the platform\n\n")
+ stagingFile.write(" NOTE: After this version of the platform is forked, changes cannot be made to the root\n")
+ stagingFile.write(" branch's groups for that release. Only merge changes to the forked platform branch.\n")
+ stagingFile.write(" =============================================================== -->\n")
+ stagingFile.write(" <eat-comment/>\n\n")
+
+ # Seed the next release's staging-public-groups as empty declarations,
+ # so its easy for another developer to expose a new public resource
+ nextId = _lowest_staging_first_id - 0x00010000
+ for resType in resTypes:
+ stagingFile.write(' <staging-public-group type="%s" first-id="%s">\n'
+ ' </staging-public-group>\n\n' %
+ (resType, '0x{0:0{1}x}'.format(nextId, 8)))
+ nextId -= 0x00010000
+
+ # Close the resources tag and truncate, since the file will be shorter than the previous
+ stagingFile.write("</resources>\n")
+ stagingFile.truncate()
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 5d57de6a9fb1..3285d8bafa4d 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -50,12 +50,12 @@ namespace file {
FileType GetFileType(const std::string& path) {
std::wstring path_utf16;
if (!::android::base::UTF8PathToWindowsLongPath(path.c_str(), &path_utf16)) {
- return FileType::kNonexistant;
+ return FileType::kNonExistant;
}
DWORD result = GetFileAttributesW(path_utf16.c_str());
if (result == INVALID_FILE_ATTRIBUTES) {
- return FileType::kNonexistant;
+ return FileType::kNonExistant;
}
if (result & FILE_ATTRIBUTE_DIRECTORY) {
@@ -72,7 +72,7 @@ FileType GetFileType(const std::string& path) {
if (result == -1) {
if (errno == ENOENT || errno == ENOTDIR) {
- return FileType::kNonexistant;
+ return FileType::kNonExistant;
}
return FileType::kUnknown;
}
@@ -154,7 +154,7 @@ StringPiece GetFilename(const StringPiece& path) {
const char* end = path.end();
const char* last_dir_sep = path.begin();
for (const char* c = path.begin(); c != end; ++c) {
- if (*c == sDirSep) {
+ if (*c == sDirSep || *c == sInvariantDirSep) {
last_dir_sep = c + 1;
}
}
@@ -208,7 +208,7 @@ std::string PackageToPath(const StringPiece& package) {
return out_path;
}
-Maybe<FileMap> MmapPath(const std::string& path, std::string* out_error) {
+std::optional<FileMap> MmapPath(const std::string& path, std::string* out_error) {
int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
unique_fd fd(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), flags)));
if (fd == -1) {
@@ -344,12 +344,12 @@ bool FileFilter::operator()(const std::string& filename, FileType type) const {
return true;
}
-Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
- const FileFilter* filter) {
+std::optional<std::vector<std::string>> FindFiles(const android::StringPiece& path,
+ IDiagnostics* diag, const FileFilter* filter) {
const std::string root_dir = path.to_string();
std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
if (!d) {
- diag->Error(DiagMessage() << SystemErrorCodeToString(errno));
+ diag->Error(DiagMessage() << SystemErrorCodeToString(errno) << ": " << root_dir);
return {};
}
@@ -382,7 +382,7 @@ Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDia
for (const std::string& subdir : subdirs) {
std::string full_subdir = root_dir;
AppendPath(&full_subdir, subdir);
- Maybe<std::vector<std::string>> subfiles = FindFiles(full_subdir, diag, filter);
+ std::optional<std::vector<std::string>> subfiles = FindFiles(full_subdir, diag, filter);
if (!subfiles) {
return {};
}
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 481a4cdb6ad0..a2b1b58e5d4f 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -18,6 +18,7 @@
#define AAPT_FILES_H
#include <memory>
+#include <optional>
#include <string>
#include <unordered_set>
#include <vector>
@@ -27,7 +28,6 @@
#include "utils/FileMap.h"
#include "Diagnostics.h"
-#include "Maybe.h"
#include "Source.h"
namespace aapt {
@@ -41,9 +41,11 @@ constexpr const char sDirSep = '/';
constexpr const char sPathSep = ':';
#endif
+constexpr const char sInvariantDirSep = '/';
+
enum class FileType {
kUnknown = 0,
- kNonexistant,
+ kNonExistant,
kRegular,
kDirectory,
kCharDev,
@@ -81,7 +83,7 @@ bool IsHidden(const android::StringPiece& path);
std::string PackageToPath(const android::StringPiece& package);
// Creates a FileMap for the file at path.
-Maybe<android::FileMap> MmapPath(const std::string& path, std::string* out_error);
+std::optional<android::FileMap> MmapPath(const std::string& path, std::string* out_error);
// Reads the file at path and appends each line to the outArgList vector.
bool AppendArgsFromFile(const android::StringPiece& path, std::vector<std::string>* out_arglist,
@@ -124,8 +126,9 @@ class FileFilter {
// Returns a list of files relative to the directory identified by `path`.
// An optional FileFilter filters out any files that don't pass.
-Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
- const FileFilter* filter = nullptr);
+std::optional<std::vector<std::string>> FindFiles(const android::StringPiece& path,
+ IDiagnostics* diag,
+ const FileFilter* filter = nullptr);
} // namespace file
} // namespace aapt
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
deleted file mode 100644
index 047e1a581330..000000000000
--- a/tools/aapt2/util/Maybe.h
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2015 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_MAYBE_H
-#define AAPT_MAYBE_H
-
-#include <type_traits>
-#include <utility>
-
-#include "android-base/logging.h"
-
-#include "util/TypeTraits.h"
-
-namespace aapt {
-
-/**
- * Either holds a valid value of type T, or holds Nothing.
- * The value is stored inline in this structure, so no
- * heap memory is used when creating a Maybe<T> object.
- */
-template <typename T>
-class Maybe {
- public:
- /**
- * Construct Nothing.
- */
- Maybe();
-
- ~Maybe();
-
- Maybe(const Maybe& rhs);
-
- template <typename U>
- Maybe(const Maybe<U>& rhs); // NOLINT(google-explicit-constructor)
-
- Maybe(Maybe&& rhs) noexcept;
-
- template <typename U>
- Maybe(Maybe<U>&& rhs); // NOLINT(google-explicit-constructor)
-
- Maybe& operator=(const Maybe& rhs);
-
- template <typename U>
- Maybe& operator=(const Maybe<U>& rhs);
-
- Maybe& operator=(Maybe&& rhs) noexcept;
-
- template <typename U>
- Maybe& operator=(Maybe<U>&& rhs);
-
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(const T& value); // NOLINT(google-explicit-constructor)
-
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(T&& value); // NOLINT(google-explicit-constructor)
-
- /**
- * True if this holds a value, false if
- * it holds Nothing.
- */
- explicit operator bool() const;
-
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- T& value();
-
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- const T& value() const;
-
- T value_or_default(const T& def) const;
-
- private:
- template <typename U>
- friend class Maybe;
-
- template <typename U>
- Maybe& copy(const Maybe<U>& rhs);
-
- template <typename U>
- Maybe& move(Maybe<U>&& rhs);
-
- void destroy();
-
- bool nothing_;
-
- typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
-};
-
-template <typename T>
-Maybe<T>::Maybe() : nothing_(true) {}
-
-template <typename T>
-Maybe<T>::~Maybe() {
- if (!nothing_) {
- destroy();
- }
-}
-
-template <typename T>
-Maybe<T>::Maybe(const Maybe& rhs) : nothing_(rhs.nothing_) {
- if (!rhs.nothing_) {
- new (&storage_) T(reinterpret_cast<const T&>(rhs.storage_));
- }
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>::Maybe(const Maybe<U>& rhs) : nothing_(rhs.nothing_) {
- if (!rhs.nothing_) {
- new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
- }
-}
-
-template <typename T>
-Maybe<T>::Maybe(Maybe&& rhs) noexcept : nothing_(rhs.nothing_) {
- if (!rhs.nothing_) {
- rhs.nothing_ = true;
-
- // Move the value from rhs.
- new (&storage_) T(std::move(reinterpret_cast<T&>(rhs.storage_)));
- rhs.destroy();
- }
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>::Maybe(Maybe<U>&& rhs) : nothing_(rhs.nothing_) {
- if (!rhs.nothing_) {
- rhs.nothing_ = true;
-
- // Move the value from rhs.
- new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
- rhs.destroy();
- }
-}
-
-template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
- // Delegate to the actual assignment.
- return copy(rhs);
-}
-
-template <typename T>
-template <typename U>
-inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
- return copy(rhs);
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
- if (nothing_ && rhs.nothing_) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!nothing_ && !rhs.nothing_) {
- // We both are something, so assign rhs to us.
- reinterpret_cast<T&>(storage_) = reinterpret_cast<const U&>(rhs.storage_);
- } else if (nothing_) {
- // We are nothing but rhs is something.
- nothing_ = rhs.nothing_;
-
- // Copy the value from rhs.
- new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
- } else {
- // We are something but rhs is nothing, so destroy our value.
- nothing_ = rhs.nothing_;
- destroy();
- }
- return *this;
-}
-
-template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) noexcept {
- // Delegate to the actual assignment.
- return move(std::forward<Maybe<T>>(rhs));
-}
-
-template <typename T>
-template <typename U>
-inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
- return move(std::forward<Maybe<U>>(rhs));
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
- if (nothing_ && rhs.nothing_) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!nothing_ && !rhs.nothing_) {
- // We both are something, so move assign rhs to us.
- rhs.nothing_ = true;
- reinterpret_cast<T&>(storage_) =
- std::move(reinterpret_cast<U&>(rhs.storage_));
- rhs.destroy();
- } else if (nothing_) {
- // We are nothing but rhs is something.
- nothing_ = false;
- rhs.nothing_ = true;
-
- // Move the value from rhs.
- new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
- rhs.destroy();
- } else {
- // We are something but rhs is nothing, so destroy our value.
- nothing_ = true;
- destroy();
- }
- return *this;
-}
-
-template <typename T>
-Maybe<T>::Maybe(const T& value) : nothing_(false) {
- new (&storage_) T(value);
-}
-
-template <typename T>
-Maybe<T>::Maybe(T&& value) : nothing_(false) {
- new (&storage_) T(std::forward<T>(value));
-}
-
-template <typename T>
-Maybe<T>::operator bool() const {
- return !nothing_;
-}
-
-template <typename T>
-T& Maybe<T>::value() {
- CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
- return reinterpret_cast<T&>(storage_);
-}
-
-template <typename T>
-const T& Maybe<T>::value() const {
- CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
- return reinterpret_cast<const T&>(storage_);
-}
-
-template <typename T>
-T Maybe<T>::value_or_default(const T& def) const {
- if (nothing_) {
- return def;
- }
- return reinterpret_cast<const T&>(storage_);
-}
-
-template <typename T>
-void Maybe<T>::destroy() {
- reinterpret_cast<T&>(storage_).~T();
-}
-
-template <typename T>
-inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
- return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
-}
-
-template <typename T>
-inline Maybe<T> make_nothing() {
- return Maybe<T>();
-}
-
-// Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
-// That way the compiler will show an error at the callsite when comparing two Maybe<> objects
-// whose inner types can't be compared.
-template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
- const Maybe<U>& b) {
- if (a && b) {
- return a.value() == b.value();
- } else if (!a && !b) {
- return true;
- }
- return false;
-}
-
-template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
- const U& b) {
- return a ? a.value() == b : false;
-}
-
-// Same as operator== but negated.
-template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=(const Maybe<T>& a,
- const Maybe<U>& b) {
- return !(a == b);
-}
-
-template <typename T, typename U>
-typename std::enable_if<has_lt_op<T, U>::value, bool>::type operator<(const Maybe<T>& a,
- const Maybe<U>& b) {
- if (a && b) {
- return a.value() < b.value();
- } else if (!a && !b) {
- return false;
- }
- return !a;
-}
-
-} // namespace aapt
-
-#endif // AAPT_MAYBE_H
diff --git a/tools/aapt2/util/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
deleted file mode 100644
index 4c921f13a3ca..000000000000
--- a/tools/aapt2/util/Maybe_test.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2015 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 "util/Maybe.h"
-
-#include <string>
-
-#include "test/Test.h"
-
-namespace aapt {
-
-struct Fake {
- Fake() {
- data = new int;
- *data = 1;
- std::cerr << "Construct Fake{0x" << (void*)this << "} with data=0x"
- << (void*)data << std::endl;
- }
-
- Fake(const Fake& rhs) {
- data = nullptr;
- if (rhs.data) {
- data = new int;
- *data = *rhs.data;
- }
- std::cerr << "CopyConstruct Fake{0x" << (void*)this << "} from Fake{0x"
- << (const void*)&rhs << "}" << std::endl;
- }
-
- Fake(Fake&& rhs) {
- data = rhs.data;
- rhs.data = nullptr;
- std::cerr << "MoveConstruct Fake{0x" << (void*)this << "} from Fake{0x"
- << (const void*)&rhs << "}" << std::endl;
- }
-
- Fake& operator=(const Fake& rhs) {
- delete data;
- data = nullptr;
-
- if (rhs.data) {
- data = new int;
- *data = *rhs.data;
- }
- std::cerr << "CopyAssign Fake{0x" << (void*)this << "} from Fake{0x"
- << (const void*)&rhs << "}" << std::endl;
- return *this;
- }
-
- Fake& operator=(Fake&& rhs) {
- delete data;
- data = rhs.data;
- rhs.data = nullptr;
- std::cerr << "MoveAssign Fake{0x" << (void*)this << "} from Fake{0x"
- << (const void*)&rhs << "}" << std::endl;
- return *this;
- }
-
- ~Fake() {
- std::cerr << "Destruct Fake{0x" << (void*)this << "} with data=0x"
- << (void*)data << std::endl;
- delete data;
- }
-
- int* data;
-};
-
-TEST(MaybeTest, MakeNothing) {
- Maybe<int> val = make_nothing<int>();
- EXPECT_FALSE(val);
-
- Maybe<std::string> val2 = make_nothing<std::string>();
- EXPECT_FALSE(val2);
-
- val2 = make_nothing<std::string>();
- EXPECT_FALSE(val2);
-}
-
-TEST(MaybeTest, MakeSomething) {
- Maybe<int> val = make_value(23);
- ASSERT_TRUE(val);
- EXPECT_EQ(23, val.value());
-
- Maybe<std::string> val2 = make_value(std::string("hey"));
- ASSERT_TRUE(val2);
- EXPECT_EQ(std::string("hey"), val2.value());
-}
-
-TEST(MaybeTest, Lifecycle) {
- Maybe<Fake> val = make_nothing<Fake>();
-
- Maybe<Fake> val2 = make_value(Fake());
-}
-
-TEST(MaybeTest, MoveAssign) {
- Maybe<Fake> val;
- {
- Maybe<Fake> val2 = Fake();
- val = std::move(val2);
- }
-}
-
-TEST(MaybeTest, Equality) {
- Maybe<int> a = 1;
- Maybe<int> b = 1;
- Maybe<int> c;
-
- Maybe<int> emptyA, emptyB;
-
- EXPECT_EQ(a, b);
- EXPECT_EQ(b, a);
- EXPECT_NE(a, c);
- EXPECT_EQ(emptyA, emptyB);
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index d7a8e6fe6ada..efbbf8ebe013 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -22,13 +22,12 @@
#include <vector>
#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
#include "androidfw/StringPiece.h"
#include "build/version.h"
-
#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
#include "util/BigBuffer.h"
-#include "util/Maybe.h"
#include "utils/Unicode.h"
using ::aapt::text::Utf8Iterator;
@@ -193,8 +192,8 @@ bool IsAndroidSplitName(const StringPiece& str) {
return IsAndroidNameImpl(str) > 0;
}
-Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
- const StringPiece& classname) {
+std::optional<std::string> GetFullyQualifiedClassName(const StringPiece& package,
+ const StringPiece& classname) {
if (classname.empty()) {
return {};
}
@@ -232,7 +231,14 @@ std::string GetToolFingerprint() {
static const char* const sMinorVersion = "19";
// The build id of aapt2 binary.
- static const std::string sBuildId = android::build::GetBuildNumber();
+ static std::string sBuildId = android::build::GetBuildNumber();
+
+ if (android::base::StartsWith(sBuildId, "eng.")) {
+ time_t now = time(0);
+ tm* ltm = localtime(&now);
+
+ sBuildId = android::base::StringPrintf("eng.%d%d", 1900 + ltm->tm_year, 1 + ltm->tm_mon);
+ }
return android::base::StringPrintf("%s.%s-%s", sMajorVersion, sMinorVersion, sBuildId.c_str());
}
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index c77aca31a810..c3efe6a63feb 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -28,7 +28,6 @@
#include "utils/ByteOrder.h"
#include "util/BigBuffer.h"
-#include "util/Maybe.h"
#ifdef _WIN32
// TODO(adamlesinski): remove once http://b/32447322 is resolved.
@@ -105,8 +104,8 @@ bool IsAndroidSharedUserId(const android::StringPiece& package_name,
// .asdf --> package.asdf
// .a.b --> package.a.b
// asdf.adsf --> asdf.adsf
-Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
- const android::StringPiece& class_name);
+std::optional<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
+ const android::StringPiece& class_name);
// Retrieves the formatted name of aapt2.
const char* GetToolName();
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 2cdcfe45b50e..8b7eadf9fac9 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -545,7 +545,7 @@ void Text::Accept(ConstVisitor* visitor) const {
void PackageAwareVisitor::BeforeVisitElement(Element* el) {
std::vector<PackageDecl> decls;
for (const NamespaceDecl& decl : el->namespace_decls) {
- if (Maybe<ExtractedPackage> maybe_package = ExtractPackageFromNamespace(decl.uri)) {
+ if (std::optional<ExtractedPackage> maybe_package = ExtractPackageFromNamespace(decl.uri)) {
decls.push_back(PackageDecl{decl.prefix, std::move(maybe_package.value())});
}
}
@@ -556,7 +556,8 @@ void PackageAwareVisitor::AfterVisitElement(Element* el) {
package_decls_.pop_back();
}
-Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(const StringPiece& alias) const {
+std::optional<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
+ const StringPiece& alias) const {
if (alias.empty()) {
return ExtractedPackage{{}, false /*private*/};
}
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index a5b2d10fc9e0..5d31804d43b7 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -65,12 +65,12 @@ struct NamespaceDecl {
};
struct AaptAttribute {
- explicit AaptAttribute(const ::aapt::Attribute& attr, const Maybe<ResourceId>& resid = {})
+ explicit AaptAttribute(const ::aapt::Attribute& attr, const std::optional<ResourceId>& resid = {})
: attribute(attr), id(resid) {
}
aapt::Attribute attribute;
- Maybe<ResourceId> id;
+ std::optional<ResourceId> id;
};
// An XML attribute.
@@ -79,7 +79,7 @@ struct Attribute {
std::string name;
std::string value;
- Maybe<AaptAttribute> compiled_attribute;
+ std::optional<AaptAttribute> compiled_attribute;
std::unique_ptr<Item> compiled_value;
};
@@ -235,7 +235,8 @@ class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
public:
using Visitor::Visit;
- Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
+ std::optional<ExtractedPackage> TransformPackageAlias(
+ const android::StringPiece& alias) const override;
protected:
PackageAwareVisitor() = default;
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index ca46d539fe3c..6c717dcd84c8 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -98,7 +98,7 @@ TEST(XmlDomTest, BinaryInflate) {
// the Attribute accepts (eg: string|reference).
ASSERT_TRUE(new_doc->root->attributes[0].compiled_attribute);
EXPECT_THAT(new_doc->root->attributes[0].compiled_attribute.value().id,
- Eq(make_value(ResourceId(0x01010001u))));
+ Eq(ResourceId(0x01010001u)));
EXPECT_THAT(new_doc->root->attributes[0].value, StrEq("@string/foo"));
EXPECT_THAT(new_doc->root->attributes[0].compiled_value,
@@ -145,21 +145,19 @@ class TestVisitor : public PackageAwareVisitor {
void Visit(Element* el) override {
if (el->name == "View1") {
- EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
} else if (el->name == "View2") {
- 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("one"), Eq(ExtractedPackage{"com.one", false}));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false}));
} else if (el->name == "View3") {
- 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})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false}));
+ EXPECT_THAT(TransformPackageAlias("three"), Eq(ExtractedPackage{"com.three", false}));
} else if (el->name == "View4") {
- 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})));
- EXPECT_THAT(TransformPackageAlias("four"), Eq(make_value(ExtractedPackage{"", true})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false}));
+ EXPECT_THAT(TransformPackageAlias("three"), Eq(ExtractedPackage{"com.three", false}));
+ EXPECT_THAT(TransformPackageAlias("four"), Eq(ExtractedPackage{"", true}));
}
}
};
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 182203d397c3..bfa07490b9c0 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -17,7 +17,6 @@
#include <iostream>
#include <string>
-#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlPullParser.h"
#include "xml/XmlUtil.h"
@@ -84,8 +83,7 @@ XmlPullParser::Event XmlPullParser::Next() {
// handling of references that use namespace aliases.
if (next_event == Event::kStartNamespace ||
next_event == Event::kEndNamespace) {
- Maybe<ExtractedPackage> result =
- ExtractPackageFromNamespace(namespace_uri());
+ std::optional<ExtractedPackage> result = ExtractPackageFromNamespace(namespace_uri());
if (next_event == Event::kStartNamespace) {
if (result) {
package_aliases_.emplace_back(
@@ -142,7 +140,8 @@ const std::string& XmlPullParser::namespace_uri() const {
return event_queue_.front().data2;
}
-Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(const StringPiece& alias) const {
+std::optional<ExtractedPackage> XmlPullParser::TransformPackageAlias(
+ const StringPiece& alias) const {
if (alias.empty()) {
return ExtractedPackage{{}, false /*private*/};
}
@@ -308,8 +307,7 @@ void XMLCALL XmlPullParser::EndCdataSectionHandler(void* user_data) {
parser->depth_ });
}
-Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
- const StringPiece& name) {
+std::optional<StringPiece> FindAttribute(const XmlPullParser* parser, const StringPiece& name) {
auto iter = parser->FindAttribute("", name);
if (iter != parser->end_attributes()) {
return StringPiece(util::TrimWhitespace(iter->value));
@@ -317,8 +315,8 @@ Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
return {};
}
-Maybe<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
- const StringPiece& name) {
+std::optional<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
+ const StringPiece& name) {
auto iter = parser->FindAttribute("", name);
if (iter != parser->end_attributes()) {
StringPiece trimmed = util::TrimWhitespace(iter->value);
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 5da2d4b10a4b..ab347728ae4b 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -33,7 +33,6 @@
#include "Resource.h"
#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
-#include "util/Maybe.h"
#include "xml/XmlUtil.h"
namespace aapt {
@@ -121,7 +120,8 @@ 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 override;
+ std::optional<ExtractedPackage> TransformPackageAlias(
+ const android::StringPiece& alias) const override;
struct PackageDecl {
std::string prefix;
@@ -193,16 +193,16 @@ class XmlPullParser : public IPackageDeclStack {
/**
* Finds the attribute in the current element within the global namespace.
*/
-Maybe<android::StringPiece> FindAttribute(const XmlPullParser* parser,
- const android::StringPiece& name);
+std::optional<android::StringPiece> FindAttribute(const XmlPullParser* parser,
+ const android::StringPiece& name);
/**
* Finds the attribute in the current element within the global namespace. The
* attribute's value
* must not be the empty string.
*/
-Maybe<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
- const android::StringPiece& name);
+std::optional<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
+ const android::StringPiece& name);
//
// Implementation
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index 0a622b2bd336..114b5ba7ab1a 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -19,7 +19,6 @@
#include <algorithm>
#include <string>
-#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
@@ -34,8 +33,7 @@ std::string BuildPackageNamespace(const StringPiece& package, bool private_refer
return result;
}
-Maybe<ExtractedPackage> ExtractPackageFromNamespace(
- const std::string& namespace_uri) {
+std::optional<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri) {
if (util::StartsWith(namespace_uri, kSchemaPublicPrefix)) {
StringPiece schema_prefix = kSchemaPublicPrefix;
StringPiece package = namespace_uri;
@@ -62,7 +60,7 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(
void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref) {
if (in_ref->name) {
- if (Maybe<ExtractedPackage> transformed_package =
+ if (std::optional<ExtractedPackage> transformed_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);
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 592a604f87a6..1ab05a93d314 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -20,7 +20,6 @@
#include <string>
#include "ResourceValues.h"
-#include "util/Maybe.h"
namespace aapt {
namespace xml {
@@ -53,7 +52,7 @@ struct ExtractedPackage {
//
// 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);
+std::optional<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
// Returns an XML Android namespace for the given package of the form:
// http://schemas.android.com/apk/res/<package>
@@ -69,7 +68,7 @@ struct IPackageDeclStack {
virtual ~IPackageDeclStack() = default;
// Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
- virtual Maybe<ExtractedPackage> TransformPackageAlias(
+ virtual std::optional<ExtractedPackage> TransformPackageAlias(
const android::StringPiece& alias) const = 0;
};
diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp
index cbded8ffac8e..7b6ce9e96689 100644
--- a/tools/aapt2/xml/XmlUtil_test.cpp
+++ b/tools/aapt2/xml/XmlUtil_test.cpp
@@ -27,7 +27,7 @@ TEST(XmlUtilTest, ExtractPackageFromNamespace) {
ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/"));
ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/prv/res/"));
- Maybe<xml::ExtractedPackage> p =
+ std::optional<xml::ExtractedPackage> p =
xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/a");
ASSERT_TRUE(p);
EXPECT_EQ(std::string("a"), p.value().package);
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 81d35efaf29f..95b43cdf253d 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -1,14 +1,28 @@
#!/bin/bash
LOCAL_DIR="$( dirname "${BASH_SOURCE}" )"
-if git branch -vv | grep -q -P "^\*[^\[]+\[aosp/"; then
+if git log -n 1 --format='%D' HEAD@{upstream} | grep -q aosp/; then
# Change appears to be in AOSP
exit 0
elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then
# Change is explicitly marked as ok to skip AOSP
exit 0
else
- # Change appears to be non-AOSP; search for files
+ # Change appears to be non-AOSP.
+
+ # If this is a cherry-pick, then allow it.
+ cherrypick=0
+ while read -r line ; do
+ if [[ $line =~ cherry\ picked\ from ]] ; then
+ (( cherrypick++ ))
+ fi
+ done < <(git show $1)
+ if (( cherrypick != 0 )); then
+ # This is a cherry-pick, so allow it.
+ exit 0
+ fi
+
+ # See if any files are affected.
count=0
while read -r file ; do
if (( count == 0 )); then
@@ -21,6 +35,7 @@ else
echo
echo "If your change contains no confidential details (such as security fixes), please"
echo "upload and merge this change at https://android-review.googlesource.com/."
+ echo "Else add a tag 'Ignore-AOSP-First:' with the reason to bypass AOSP."
echo
exit 1
fi
diff --git a/tools/apilint/deprecated_at_birth.py b/tools/apilint/deprecated_at_birth.py
index 297d9c3bcca0..d53c12734d23 100644..100755
--- a/tools/apilint/deprecated_at_birth.py
+++ b/tools/apilint/deprecated_at_birth.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2021 The Android Open Source Project
#
@@ -44,6 +44,7 @@ def ident(raw):
can be used to identify members across API levels."""
raw = raw.replace(" deprecated ", " ")
raw = raw.replace(" synchronized ", " ")
+ raw = raw.replace(" abstract ", " ")
raw = raw.replace(" final ", " ")
raw = re.sub("<.+?>", "", raw)
raw = re.sub("@[A-Za-z]+ ", "", raw)
@@ -208,17 +209,17 @@ def _parse_stream(f, api={}):
def _parse_stream_path(path):
api = {}
- print "Parsing", path
+ print("Parsing %s" % path)
for f in os.listdir(path):
f = os.path.join(path, f)
if not os.path.isfile(f): continue
if not f.endswith(".txt"): continue
if f.endswith("removed.txt"): continue
- print "\t", f
+ print("\t%s" % f)
with open(f) as s:
api = _parse_stream(s, api)
- print "Parsed", len(api), "APIs"
- print
+ print("Parsed %d APIs" % len(api))
+ print()
return api
@@ -306,8 +307,8 @@ if __name__ == "__main__":
if "@Deprecated " in i.raw:
error(clazz, i, None, "Found API deprecation at birth " + i.ident)
- print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
- format(reset=True)))
+ print("%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
+ format(reset=True))))
for f in sorted(failures):
- print failures[f]
- print
+ print(failures[f])
+ print()
diff --git a/tools/bit/command.cpp b/tools/bit/command.cpp
index f95ea117a96e..6c68e0b0ff6b 100644
--- a/tools/bit/command.cpp
+++ b/tools/bit/command.cpp
@@ -192,10 +192,11 @@ exec_with_path_search(const char* prog, char const* const* argv, char const* con
if (strchr(prog, '/') != NULL) {
return execve(prog, (char*const*)argv, (char*const*)envp);
} else {
- char* pathEnv = strdup(getenv("PATH"));
- if (pathEnv == NULL) {
+ const char* pathEnvRaw = getenv("PATH");
+ if (pathEnvRaw == NULL) {
return 1;
}
+ char* pathEnv = strdup(pathEnvRaw);
char* dir = pathEnv;
while (dir) {
char* next = strchr(dir, ':');
diff --git a/tools/bit/print.cpp b/tools/bit/print.cpp
index 35feda11ec29..8bc6f167bd7f 100644
--- a/tools/bit/print.cpp
+++ b/tools/bit/print.cpp
@@ -17,6 +17,7 @@
#include "print.h"
#include <sys/ioctl.h>
+#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
diff --git a/tools/bit/util.h b/tools/bit/util.h
index 7ccdab103d9a..8c66911b3c48 100644
--- a/tools/bit/util.h
+++ b/tools/bit/util.h
@@ -17,6 +17,8 @@
#ifndef UTIL_H
#define UTIL_H
+#include <sys/types.h>
+
#include <map>
#include <string>
#include <vector>
diff --git a/tools/codegen/OWNERS b/tools/codegen/OWNERS
index da723b3b67da..c9bd260ca7ae 100644
--- a/tools/codegen/OWNERS
+++ b/tools/codegen/OWNERS
@@ -1 +1 @@
-eugenesusla@google.com \ No newline at end of file
+chiuwinson@google.com
diff --git a/tools/codegen/src/com/android/codegen/ImportsProvider.kt b/tools/codegen/src/com/android/codegen/ImportsProvider.kt
index 27dd9587db25..46df2739e59f 100644
--- a/tools/codegen/src/com/android/codegen/ImportsProvider.kt
+++ b/tools/codegen/src/com/android/codegen/ImportsProvider.kt
@@ -53,10 +53,25 @@ interface ImportsProvider {
* Optionally shortens a class reference if there's a corresponding import present
*/
fun classRef(fullName: String): String {
-
val pkg = fullName.substringBeforeLast(".")
val simpleName = fullName.substringAfterLast(".")
- if (fileAst.imports.any { imprt ->
+ val imports = fileAst.imports
+
+ // If an import of the same class name is available,
+ // use it instead of the internal Android package variants.
+ if (fullName.startsWith("com.android.internal.util.")
+ && imports.any {
+ it.nameAsString.endsWith(fullName.removePrefix("com.android.internal.util."))
+ }
+ ) {
+ return fullName.removePrefix("com.android.internal.util.")
+ } else if (fullName.startsWith("android.annotation")
+ && imports.any { it.nameAsString.endsWith(simpleName) }
+ ) {
+ return simpleName
+ }
+
+ if (imports.any { imprt ->
imprt.nameAsString == fullName
|| (imprt.isAsterisk && imprt.nameAsString == pkg)
}) {
@@ -89,4 +104,4 @@ interface ImportsProvider {
/** @see classRef */
inline fun <reified T : Any> ImportsProvider.classRef(): String {
return classRef(T::class.java.name)
-} \ No newline at end of file
+}
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index 7cfa7847fcff..9ceb2042d74e 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -43,8 +43,8 @@ inline infix fun Int.times(action: () -> Unit) {
* cccc dd
*/
fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String {
- val col1w = map { (a, _) -> a.length }.max()!!
- val col2w = map { (_, b) -> b.length }.max()!!
+ val col1w = map { (a, _) -> a.length }.maxOrNull()!!
+ val col2w = map { (_, b) -> b.length }.maxOrNull()!!
return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n")
}
diff --git a/tools/finalize_res/finalize_res.py b/tools/finalize_res/finalize_res.py
deleted file mode 100755
index 724443c01852..000000000000
--- a/tools/finalize_res/finalize_res.py
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env python3
-#-*- coding: utf-8 -*-
-
-# Copyright (C) 2021 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.
-
-"""
-Finalize resource values in <staging-public-group> tags
-and convert those to <staging-public-group-final>
-
-Usage: finalize_res.py core/res/res/values/public.xml public_finalized.xml
-"""
-
-import re, sys, codecs
-
-def finalize_item(raw):
- global _type_ids, _type
- id = _type_ids[_type]
- _type_ids[_type] += 1
- name = raw.group(1)
- val = '<public type="%s" name="%s" id="%s" />' % (_type, name, '0x{0:0{1}x}'.format(id,8))
- if re.match(r'_*removed.+', name):
- val = '<!-- ' + val.replace('<public', '< public') + ' -->'
- return val
-
-def finalize_group(raw):
- global _type_ids, _type
- _type = raw.group(1)
- id = int(raw.group(2), 16)
- _type_ids[_type] = _type_ids.get(_type, id)
- (res, count) = re.subn(r' {0,2}<public name="(.+?)" */>', finalize_item, raw.group(3))
- if count > 0:
- res = raw.group(0).replace("staging-public-group", "staging-public-group-final") + '\n' + res
- return res
-
-def collect_ids(raw):
- global _type_ids
- for m in re.finditer(r'<public type="(.+?)" name=".+?" id="(.+?)" />', raw):
- type = m.group(1)
- id = int(m.group(2), 16)
- _type_ids[type] = max(id + 1, _type_ids.get(type, 0))
-
-with open(sys.argv[1]) as f:
- global _type_ids, _type
- _type_ids = {}
- raw = f.read()
- collect_ids(raw)
- raw = re.sub(r'<staging-public-group type="(.+?)" first-id="(.+?)">(.+?)</staging-public-group>', finalize_group, raw, flags=re.DOTALL)
- raw = re.sub(r' *\n', '\n', raw)
- raw = re.sub(r'\n{3,}', '\n\n', raw)
- with open(sys.argv[2], "w") as f:
- f.write(raw)
-
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index e181c62c67e5..35a0ce60e359 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -13,41 +13,56 @@ from fontTools import ttLib
EMOJI_VS = 0xFE0F
LANG_TO_SCRIPT = {
+ 'af': 'Latn',
'as': 'Beng',
+ 'am': 'Latn',
'be': 'Cyrl',
'bg': 'Cyrl',
'bn': 'Beng',
+ 'cs': 'Latn',
'cu': 'Cyrl',
'cy': 'Latn',
'da': 'Latn',
'de': 'Latn',
+ 'el': 'Latn',
'en': 'Latn',
'es': 'Latn',
'et': 'Latn',
'eu': 'Latn',
'fr': 'Latn',
'ga': 'Latn',
+ 'gl': 'Latn',
'gu': 'Gujr',
'hi': 'Deva',
'hr': 'Latn',
'hu': 'Latn',
'hy': 'Armn',
+ 'it': 'Latn',
'ja': 'Jpan',
+ 'ka': 'Latn',
'kn': 'Knda',
'ko': 'Kore',
'la': 'Latn',
+ 'lt': 'Latn',
+ 'lv': 'Latn',
'ml': 'Mlym',
'mn': 'Cyrl',
'mr': 'Deva',
'nb': 'Latn',
+ 'nl': 'Latn',
'nn': 'Latn',
'or': 'Orya',
'pa': 'Guru',
'pt': 'Latn',
+ 'ru': 'Latn',
+ 'sk': 'Latn',
'sl': 'Latn',
+ 'sq': 'Latn',
+ 'sv': 'Latn',
'ta': 'Taml',
'te': 'Telu',
'tk': 'Latn',
+ 'uk': 'Latn',
}
def lang_to_script(lang_code):
@@ -228,6 +243,8 @@ def parse_fonts_xml(fonts_xml_path):
name = family.get('name')
variant = family.get('variant')
langs = family.get('lang')
+ ignoreAttr = family.get('ignore')
+
if name:
assert variant is None, (
'No variant expected for LGC font %s.' % name)
@@ -244,6 +261,11 @@ def parse_fonts_xml(fonts_xml_path):
name = family.get('name')
variant = family.get('variant')
langs = family.get('lang')
+ ignoreAttr = family.get('ignore')
+ ignore = ignoreAttr == 'true' or ignoreAttr == '1'
+
+ if ignore:
+ continue
if langs:
langs = langs.split()
@@ -318,29 +340,104 @@ def check_emoji_coverage(all_emoji, equivalent_emoji):
def get_emoji_fonts():
return [ record.font for record in _all_fonts if 'Zsye' in record.scripts ]
+def seq_any(sequence, pred):
+ if type(sequence) is tuple:
+ return any([pred(x) for x in sequence])
+ else:
+ return pred(sequence)
+
+def seq_all(sequence, pred):
+ if type(sequence) is tuple:
+ return all([pred(x) for x in sequence])
+ else:
+ return pred(sequence)
+
+def is_regional_indicator(x):
+ # regional indicator A..Z
+ return 0x1F1E6 <= x <= 0x1F1FF
+
+def is_tag(x):
+ # tag block
+ return 0xE0000 <= x <= 0xE007F
+
def is_pua(x):
return 0xE000 <= x <= 0xF8FF or 0xF0000 <= x <= 0xFFFFD or 0x100000 <= x <= 0x10FFFD
def contains_pua(sequence):
- if type(sequence) is tuple:
- return any([is_pua(x) for x in sequence])
- else:
- return is_pua(sequence)
+ return seq_any(sequence, is_pua)
+
+def contains_regional_indicator(sequence):
+ return seq_any(sequence, is_regional_indicator)
+
+def only_tags(sequence):
+ return seq_all(sequence, is_tag)
def get_psname(ttf):
return str(next(x for x in ttf['name'].names
if x.platformID == 3 and x.platEncID == 1 and x.nameID == 6))
-def check_emoji_compat():
+def hex_strs(sequence):
+ if type(sequence) is tuple:
+ return tuple(f"{s:X}" for s in sequence)
+ return hex(sequence)
+
+def check_plausible_compat_pua(coverage, all_emoji, equivalent_emoji):
+ # A PUA should point to every RGI emoji and that PUA should be unique to the
+ # set of equivalent sequences for the emoji.
+ problems = []
+ for seq in all_emoji:
+ # We're looking to match not-PUA with PUA so filter out existing PUA
+ if contains_pua(seq):
+ continue
+
+ # Filter out non-RGI things that end up in all_emoji
+ if only_tags(seq) or seq in {ZWJ, COMBINING_KEYCAP, EMPTY_FLAG_SEQUENCE}:
+ continue
+
+ equivalents = [seq]
+ if seq in equivalent_emoji:
+ equivalents.append(equivalent_emoji[seq])
+
+ # If there are problems the hex code is much more useful
+ log_equivalents = [hex_strs(s) for s in equivalents]
+
+ # The system compat font should NOT include regional indicators as these have been split out
+ if contains_regional_indicator(seq):
+ assert not any(s in coverage for s in equivalents), f"Regional indicators not expected in compat font, found {log_equivalents}"
+ continue
+
+ glyph = {coverage[e] for e in equivalents}
+ if len(glyph) != 1:
+ problems.append(f"{log_equivalents} should all point to the same glyph")
+ continue
+ glyph = next(iter(glyph))
+
+ pua = {s for s, g in coverage.items() if contains_pua(s) and g == glyph}
+ if not pua:
+ problems.append(f"Expected PUA for {log_equivalents} but none exist")
+ continue
+
+ assert not problems, "\n".join(sorted(problems)) + f"\n{len(problems)} PUA problems"
+
+def check_emoji_compat(all_emoji, equivalent_emoji):
+ compat_psnames = set()
for emoji_font in get_emoji_fonts():
ttf = open_font(emoji_font)
psname = get_psname(ttf)
- # If the font file is NotoColorEmoji, it must be Compat font.
- if psname == 'NotoColorEmoji':
- meta = ttf['meta']
- assert meta, 'Compat font must have meta table'
- assert 'Emji' in meta.data, 'meta table should have \'Emji\' data.'
+ is_compat_font = "meta" in ttf and 'Emji' in ttf["meta"].data
+ if not is_compat_font:
+ continue
+ compat_psnames.add(psname)
+
+ # If the font has compat metadata it should have PUAs for emoji sequences
+ coverage = get_emoji_map(emoji_font)
+ check_plausible_compat_pua(coverage, all_emoji, equivalent_emoji)
+
+
+ # NotoColorEmoji must be a Compat font.
+ assert 'NotoColorEmoji' in compat_psnames, 'NotoColorEmoji MUST be a compat font'
+
def check_emoji_font_coverage(emoji_fonts, all_emoji, equivalent_emoji):
coverages = []
@@ -589,6 +686,8 @@ SAME_FLAG_MAPPINGS = [
ZWJ = 0x200D
+EMPTY_FLAG_SEQUENCE = (0x1F3F4, 0xE007F)
+
def is_fitzpatrick_modifier(cp):
return 0x1F3FB <= cp <= 0x1F3FF
@@ -614,29 +713,17 @@ def compute_expected_emoji():
adjusted_emoji_zwj_sequences.update(_emoji_zwj_sequences)
# Add empty flag tag sequence that is supported as fallback
- _emoji_sequences[(0x1F3F4, 0xE007F)] = 'Emoji_Tag_Sequence'
+ _emoji_sequences[EMPTY_FLAG_SEQUENCE] = 'Emoji_Tag_Sequence'
for sequence in _emoji_sequences.keys():
sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
all_sequences.add(sequence)
sequence_pieces.update(sequence)
- if _emoji_sequences.get(sequence, None) == 'Emoji_Tag_Sequence':
- # Add reverse of all emoji ZWJ sequences, which are added to the
- # fonts as a workaround to get the sequences work in RTL text.
- # TODO: test if these are actually needed by Minikin/HarfBuzz.
- reversed_seq = reverse_emoji(sequence)
- all_sequences.add(reversed_seq)
- equivalent_emoji[reversed_seq] = sequence
for sequence in adjusted_emoji_zwj_sequences.keys():
sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
all_sequences.add(sequence)
sequence_pieces.update(sequence)
- # Add reverse of all emoji ZWJ sequences, which are added to the fonts
- # as a workaround to get the sequences work in RTL text.
- reversed_seq = reverse_emoji(sequence)
- all_sequences.add(reversed_seq)
- equivalent_emoji[reversed_seq] = sequence
for first, second in SAME_FLAG_MAPPINGS:
equivalent_emoji[first] = second
@@ -741,6 +828,7 @@ def main():
_fonts_dir = path.join(target_out, 'fonts')
fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml')
+
parse_fonts_xml(fonts_xml_path)
check_compact_only_fallback()
@@ -759,7 +847,7 @@ def main():
ucd_path = sys.argv[3]
parse_ucd(ucd_path)
all_emoji, default_emoji, equivalent_emoji = compute_expected_emoji()
- check_emoji_compat()
+ check_emoji_compat(all_emoji, equivalent_emoji)
check_emoji_coverage(all_emoji, equivalent_emoji)
check_emoji_defaults(default_emoji)
diff --git a/tools/lint/Android.bp b/tools/lint/Android.bp
new file mode 100644
index 000000000000..17547ef8b561
--- /dev/null
+++ b/tools/lint/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2021 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library_host {
+ name: "AndroidFrameworkLintChecker",
+ srcs: ["checks/src/main/java/**/*.kt"],
+ plugins: ["auto_service_plugin"],
+ libs: [
+ "auto_service_annotations",
+ "lint_api",
+ ],
+}
+
+java_test_host {
+ name: "AndroidFrameworkLintCheckerTest",
+ srcs: ["checks/src/test/java/**/*.kt"],
+ static_libs: [
+ "AndroidFrameworkLintChecker",
+ "junit",
+ "lint",
+ "lint_tests",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+}
diff --git a/tools/lint/OWNERS b/tools/lint/OWNERS
new file mode 100644
index 000000000000..7c0451900e32
--- /dev/null
+++ b/tools/lint/OWNERS
@@ -0,0 +1,5 @@
+brufino@google.com
+jsharkey@google.com
+
+per-file *CallingSettingsNonUserGetterMethods* = file:/packages/SettingsProvider/OWNERS
+
diff --git a/tools/lint/README.md b/tools/lint/README.md
new file mode 100644
index 000000000000..b534b62cb395
--- /dev/null
+++ b/tools/lint/README.md
@@ -0,0 +1,84 @@
+# Android Framework Lint Checker
+
+Custom lint checks written here are going to be executed for modules that opt in to those (e.g. any
+`services.XXX` module) and results will be automatically reported on CLs on gerrit.
+
+## How to add new lint checks
+
+1. Write your detector with its issues and put it into
+ `checks/src/main/java/com/google/android/lint`.
+2. Add your detector's issues into `AndroidFrameworkIssueRegistry`'s `issues` field.
+3. Write unit tests for your detector in one file and put it into
+ `checks/test/java/com/google/android/lint`.
+4. Done! Your lint checks should be applied in lint report builds for modules that include
+ `AndroidFrameworkLintChecker`.
+
+## How to run lint against your module
+
+1. Add the following `lint` attribute to the module definition, e.g. `services.autofill`:
+```
+java_library_static {
+ name: "services.autofill",
+ ...
+ lint: {
+ extra_check_modules: ["AndroidFrameworkLintChecker"],
+ },
+}
+```
+2. Run the following command to verify that the report is being correctly built:
+```
+m out/soong/.intermediates/frameworks/base/services/autofill/services.autofill/android_common/lint/lint-report.html
+```
+ (Lint report can be found in the same path, i.e. `out/../lint-report.html`)
+
+3. Now lint issues should appear on gerrit!
+
+**Notes:**
+
+- Lint report will not be produced if you just build the module, i.e. `m services.autofill` will not
+ build the lint report.
+- If you want to build lint reports for more than 1 module and they include a common module in their
+ `defaults` field, e.g. `platform_service_defaults`, you can add the `lint` property to that common
+ module instead of adding it in every module.
+- If you want to run a single lint type, use the `ANDROID_LINT_CHECK`
+ environment variable with the id of the lint. For example:
+ `ANDROID_LINT_CHECK=UnusedTokenOfOriginalCallingIdentity m out/[...]/lint-report.html`
+
+## Create or update a baseline
+
+Baseline files can be used to silence known errors (and warnings) that are deemed to be safe. When
+there is a lint-baseline.xml file in the root folder of the java library, soong will
+automatically use it. You can override the file using lint properties too.
+
+```
+java_library {
+ lint: {
+ baseline_filename: "my-baseline.xml", // default: lint-baseline.xml;
+ }
+}
+```
+
+When using soong to create a lint report (as described above), it also creates a reference
+baseline file. This contains all lint errors and warnings that were found. So the next time
+you run lint, if you use this baseline file, there should be 0 findings.
+
+After the previous invocation, you can find the baseline here:
+
+```
+out/soong/.intermediates/frameworks/base/services/autofill/services.autofill/android_common/lint/lint-baseline.xml
+```
+
+As noted above, this baseline file contains warnings too, which might be undesirable. For example,
+CI tools might surface these warnings in code reviews. In order to create this file without
+warnings, we need to pass another flag to lint: `--nowarn`. The easiest way to do this is to
+locally change the soong code in
+[lint.go](http://cs/aosp-master/build/soong/java/lint.go;l=451;rcl=2e778d5bc4a8d1d77b4f4a3029a4a254ad57db75)
+adding `cmd.Flag("--nowarn")` and running lint again.
+
+## Documentation
+
+- [Android Lint Docs](https://googlesamples.github.io/android-custom-lint-rules/)
+- [Android Lint source files](https://source.corp.google.com/studio-main/tools/base/lint/libs/lint-api/src/main/java/com/android/tools/lint/)
+- [PSI source files](https://github.com/JetBrains/intellij-community/tree/master/java/java-psi-api/src/com/intellij/psi)
+- [UAST source files](https://upsource.jetbrains.com/idea-ce/structure/idea-ce-7b9b8cc138bbd90aec26433f82cd2c6838694003/uast/uast-common/src/org/jetbrains/uast)
+- [IntelliJ plugin for viewing PSI tree of files](https://plugins.jetbrains.com/plugin/227-psiviewer)
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
new file mode 100644
index 000000000000..a6fd9bba6192
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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.google.android.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+import com.google.auto.service.AutoService
+
+@AutoService(IssueRegistry::class)
+@Suppress("UnstableApiUsage")
+class AndroidFrameworkIssueRegistry : IssueRegistry() {
+ override val issues = listOf(
+ CallingIdentityTokenDetector.ISSUE_UNUSED_TOKEN,
+ CallingIdentityTokenDetector.ISSUE_NON_FINAL_TOKEN,
+ CallingIdentityTokenDetector.ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+ CallingIdentityTokenDetector.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+ CallingIdentityTokenDetector.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+ CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
+ CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED,
+ EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
+ EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
+ )
+
+ override val api: Int
+ get() = CURRENT_API
+
+ override val minApi: Int
+ get() = 8
+
+ override val vendor: Vendor = Vendor(
+ vendorName = "Android",
+ feedbackUrl = "http://b/issues/new?component=315013",
+ contact = "brufino@google.com"
+ )
+}
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt
new file mode 100644
index 000000000000..930378b168b2
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt
@@ -0,0 +1,577 @@
+/*
+ * Copyright (C) 2021 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.google.android.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Context
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.search.PsiSearchScopeUtil
+import com.intellij.psi.search.SearchScope
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UDeclarationsExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.ULocalVariable
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.UTryExpression
+import org.jetbrains.uast.getParentOfType
+import org.jetbrains.uast.getQualifiedParentOrThis
+import org.jetbrains.uast.getUCallExpression
+import org.jetbrains.uast.skipParenthesizedExprDown
+import org.jetbrains.uast.skipParenthesizedExprUp
+
+/**
+ * Lint Detector that finds issues with improper usages of the token returned by
+ * Binder.clearCallingIdentity()
+ */
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
+ /** Map of <Token variable name, Token object> */
+ private val tokensMap = mutableMapOf<String, Token>()
+
+ override fun getApplicableUastTypes(): List<Class<out UElement?>> =
+ listOf(ULocalVariable::class.java, UCallExpression::class.java)
+
+ override fun createUastHandler(context: JavaContext): UElementHandler =
+ TokenUastHandler(context)
+
+ /** File analysis starts with a clear map */
+ override fun beforeCheckFile(context: Context) {
+ tokensMap.clear()
+ }
+
+ /**
+ * - If tokensMap has tokens after checking the file -> reports all locations as unused token
+ * issue incidents
+ * - File analysis ends with a clear map
+ */
+ override fun afterCheckFile(context: Context) {
+ for (token in tokensMap.values) {
+ context.report(
+ ISSUE_UNUSED_TOKEN,
+ token.location,
+ getIncidentMessageUnusedToken(token.variableName)
+ )
+ }
+ tokensMap.clear()
+ }
+
+ /** UAST handler that analyses elements and reports incidents */
+ private inner class TokenUastHandler(val context: JavaContext) : UElementHandler() {
+ /**
+ * For every variable initialization with Binder.clearCallingIdentity():
+ * - Checks for non-final token issue
+ * - Checks for unused token issue within different scopes
+ * - Checks for nested calls of clearCallingIdentity() issue
+ * - Checks for clearCallingIdentity() not followed by try-finally issue
+ * - Stores token variable name, scope in the file, location and finally block in tokensMap
+ */
+ override fun visitLocalVariable(node: ULocalVariable) {
+ val initializer = node.uastInitializer?.skipParenthesizedExprDown()
+ val rhsExpression = initializer?.getUCallExpression() ?: return
+ if (!isMethodCall(rhsExpression, Method.BINDER_CLEAR_CALLING_IDENTITY)) return
+ val location = context.getLocation(node as UElement)
+ val variableName = node.getName()
+ if (!node.isFinal) {
+ context.report(
+ ISSUE_NON_FINAL_TOKEN,
+ location,
+ getIncidentMessageNonFinalToken(variableName)
+ )
+ }
+ // If there exists an unused variable with the same name in the map, we can imply that
+ // we left the scope of the previous declaration, so we need to report the unused token
+ val oldToken = tokensMap[variableName]
+ if (oldToken != null) {
+ context.report(
+ ISSUE_UNUSED_TOKEN,
+ oldToken.location,
+ getIncidentMessageUnusedToken(oldToken.variableName)
+ )
+ }
+ // If there exists a token in the same scope as the current new token, it means that
+ // clearCallingIdentity() has been called at least twice without immediate restoration
+ // of identity, so we need to report the nested call of clearCallingIdentity()
+ val firstCallToken = findFirstTokenInScope(node)
+ if (firstCallToken != null) {
+ context.report(
+ ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+ createNestedLocation(firstCallToken, location),
+ getIncidentMessageNestedClearIdentityCallsPrimary(
+ firstCallToken.variableName,
+ variableName
+ )
+ )
+ }
+ // If the next statement in the tree is not a try-finally statement, we need to report
+ // the "clearCallingIdentity() is not followed by try-finally" issue
+ val finallyClause = (getNextStatementOfLocalVariable(node) as? UTryExpression)
+ ?.finallyClause
+ if (finallyClause == null) {
+ context.report(
+ ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
+ location,
+ getIncidentMessageClearIdentityCallNotFollowedByTryFinally(variableName)
+ )
+ }
+ tokensMap[variableName] = Token(
+ variableName,
+ node.sourcePsi?.getUseScope(),
+ location,
+ finallyClause
+ )
+ }
+
+ /**
+ * For every method():
+ * - Checks use of caller-aware methods issue
+ * For every call of Binder.restoreCallingIdentity(token):
+ * - Checks for restoreCallingIdentity() not in the finally block issue
+ * - Removes token from tokensMap if token is within the scope of the method
+ */
+ override fun visitCallExpression(node: UCallExpression) {
+ val token = findFirstTokenInScope(node)
+ if (isCallerAwareMethod(node) && token != null) {
+ context.report(
+ ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+ context.getLocation(node),
+ getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
+ token.variableName,
+ node.asRenderString()
+ )
+ )
+ return
+ }
+ if (!isMethodCall(node, Method.BINDER_RESTORE_CALLING_IDENTITY)) return
+ val first = node.valueArguments[0].skipParenthesizedExprDown()
+ val arg = first as? USimpleNameReferenceExpression ?: return
+ val variableName = arg.identifier
+ val originalScope = tokensMap[variableName]?.scope ?: return
+ val psi = arg.sourcePsi ?: return
+ // Checks if Binder.restoreCallingIdentity(token) is called within the scope of the
+ // token declaration. If not within the scope, no action is needed because the token is
+ // irrelevant i.e. not in the same scope or was not declared with clearCallingIdentity()
+ if (!PsiSearchScopeUtil.isInScope(originalScope, psi)) return
+ // - We do not report "restore identity call not in finally" issue when there is no
+ // finally block because that case is already handled by "clear identity call not
+ // followed by try-finally" issue
+ // - UCallExpression can be a child of UQualifiedReferenceExpression, i.e.
+ // receiver.selector, so to get the call's immediate parent we need to get the topmost
+ // parent qualified reference expression and access its parent
+ if (tokensMap[variableName]?.finallyBlock != null &&
+ skipParenthesizedExprUp(node.getQualifiedParentOrThis().uastParent) !=
+ tokensMap[variableName]?.finallyBlock) {
+ context.report(
+ ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+ context.getLocation(node),
+ getIncidentMessageRestoreIdentityCallNotInFinallyBlock(variableName)
+ )
+ }
+ tokensMap.remove(variableName)
+ }
+
+ private fun isCallerAwareMethod(expression: UCallExpression): Boolean =
+ callerAwareMethods.any { method -> isMethodCall(expression, method) }
+
+ private fun isMethodCall(
+ expression: UCallExpression,
+ method: Method
+ ): Boolean {
+ val psiMethod = expression.resolve() ?: return false
+ return psiMethod.getName() == method.methodName &&
+ context.evaluator.methodMatches(
+ psiMethod,
+ method.className,
+ /* allowInherit */ true,
+ *method.args
+ )
+ }
+
+ /**
+ * ULocalVariable in the file tree:
+ *
+ * UBlockExpression
+ * UDeclarationsExpression
+ * ULocalVariable
+ * ULocalVariable
+ * UTryStatement
+ * etc.
+ *
+ * To get the next statement of ULocalVariable:
+ * - If there exists a next sibling in UDeclarationsExpression, return the sibling
+ * - If there exists a next sibling of UDeclarationsExpression in UBlockExpression, return
+ * the sibling
+ * - Otherwise, return null
+ *
+ * Example 1 - the next sibling is in UDeclarationsExpression:
+ * Code:
+ * {
+ * int num1 = 0, num2 = methodThatThrowsException();
+ * }
+ * Returns: num2 = methodThatThrowsException()
+ *
+ * Example 2 - the next sibling is in UBlockExpression:
+ * Code:
+ * {
+ * int num1 = 0;
+ * methodThatThrowsException();
+ * }
+ * Returns: methodThatThrowsException()
+ *
+ * Example 3 - no next sibling;
+ * Code:
+ * {
+ * int num1 = 0;
+ * }
+ * Returns: null
+ */
+ private fun getNextStatementOfLocalVariable(node: ULocalVariable): UElement? {
+ val declarationsExpression = node.uastParent as? UDeclarationsExpression ?: return null
+ val declarations = declarationsExpression.declarations
+ val indexInDeclarations = declarations.indexOf(node)
+ if (indexInDeclarations != -1 && declarations.size > indexInDeclarations + 1) {
+ return declarations[indexInDeclarations + 1]
+ }
+ val enclosingBlock = node
+ .getParentOfType<UBlockExpression>(strict = true) ?: return null
+ val expressions = enclosingBlock.expressions
+ val indexInBlock = expressions.indexOf(declarationsExpression as UElement)
+ return if (indexInBlock == -1) null else expressions.getOrNull(indexInBlock + 1)
+ }
+ }
+
+ private fun findFirstTokenInScope(node: UElement): Token? {
+ val psi = node.sourcePsi ?: return null
+ for (token in tokensMap.values) {
+ if (token.scope != null && PsiSearchScopeUtil.isInScope(token.scope, psi)) {
+ return token
+ }
+ }
+ return null
+ }
+
+ /**
+ * Creates a new instance of the primary location with the secondary location
+ *
+ * Here, secondary location is the helper location that shows where the issue originated
+ *
+ * The detector reports locations as objects, so when we add a secondary location to a location
+ * that has multiple issues, the secondary location gets displayed every time a location is
+ * referenced.
+ *
+ * Example:
+ * 1: final long token1 = Binder.clearCallingIdentity();
+ * 2: long token2 = Binder.clearCallingIdentity();
+ * 3: Binder.restoreCallingIdentity(token1);
+ * 4: Binder.restoreCallingIdentity(token2);
+ *
+ * Explanation:
+ * token2 has 2 issues: NonFinal and NestedCalls
+ *
+ * Lint report without cloning Lint report with cloning
+ * line 2: [NonFinalIssue] line 2: [NonFinalIssue]
+ * line 1: [NestedCallsIssue]
+ * line 2: [NestedCallsIssue] line 2: [NestedCallsIssue]
+ * line 1: [NestedCallsIssue] line 1: [NestedCallsIssue]
+ */
+ private fun createNestedLocation(
+ firstCallToken: Token,
+ secondCallTokenLocation: Location
+ ): Location {
+ return cloneLocation(secondCallTokenLocation)
+ .withSecondary(
+ cloneLocation(firstCallToken.location),
+ getIncidentMessageNestedClearIdentityCallsSecondary(
+ firstCallToken.variableName
+ )
+ )
+ }
+
+ private fun cloneLocation(location: Location): Location {
+ // smart cast of location.start to 'Position' is impossible, because 'location.start' is a
+ // public API property declared in different module
+ val locationStart = location.start
+ return if (locationStart == null) {
+ Location.create(location.file)
+ } else {
+ Location.create(location.file, locationStart, location.end)
+ }
+ }
+
+ private enum class Method(
+ val className: String,
+ val methodName: String,
+ val args: Array<String>
+ ) {
+ BINDER_CLEAR_CALLING_IDENTITY(CLASS_BINDER, "clearCallingIdentity", emptyArray()),
+ BINDER_RESTORE_CALLING_IDENTITY(CLASS_BINDER, "restoreCallingIdentity", arrayOf("long")),
+ BINDER_GET_CALLING_PID(CLASS_BINDER, "getCallingPid", emptyArray()),
+ BINDER_GET_CALLING_UID(CLASS_BINDER, "getCallingUid", emptyArray()),
+ BINDER_GET_CALLING_UID_OR_THROW(CLASS_BINDER, "getCallingUidOrThrow", emptyArray()),
+ BINDER_GET_CALLING_USER_HANDLE(CLASS_BINDER, "getCallingUserHandle", emptyArray()),
+ USER_HANDLE_GET_CALLING_APP_ID(CLASS_USER_HANDLE, "getCallingAppId", emptyArray()),
+ USER_HANDLE_GET_CALLING_USER_ID(CLASS_USER_HANDLE, "getCallingUserId", emptyArray())
+ }
+
+ private data class Token(
+ val variableName: String,
+ val scope: SearchScope?,
+ val location: Location,
+ val finallyBlock: UElement?
+ )
+
+ companion object {
+ const val CLASS_BINDER = "android.os.Binder"
+ const val CLASS_USER_HANDLE = "android.os.UserHandle"
+
+ private val callerAwareMethods = listOf(
+ Method.BINDER_GET_CALLING_PID,
+ Method.BINDER_GET_CALLING_UID,
+ Method.BINDER_GET_CALLING_UID_OR_THROW,
+ Method.BINDER_GET_CALLING_USER_HANDLE,
+ Method.USER_HANDLE_GET_CALLING_APP_ID,
+ Method.USER_HANDLE_GET_CALLING_USER_ID
+ )
+
+ /** Issue: unused token from Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_UNUSED_TOKEN: Issue = Issue.create(
+ id = "UnusedTokenOfOriginalCallingIdentity",
+ briefDescription = "Unused token of Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but have not used the returned token to \
+ restore the identity.
+
+ Call `Binder.restoreCallingIdentity(token)` in the `finally` block, at the end \
+ of the method or when you need to restore the identity.
+
+ `token` is the result of `Binder.clearCallingIdentity()`
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageUnusedToken(variableName: String) = "`$variableName` has " +
+ "not been used to restore the calling identity. Introduce a `try`-`finally` " +
+ "after the declaration and call `Binder.restoreCallingIdentity($variableName)` " +
+ "in `finally` or remove `$variableName`."
+
+ /** Issue: non-final token from Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_NON_FINAL_TOKEN: Issue = Issue.create(
+ id = "NonFinalTokenOfOriginalCallingIdentity",
+ briefDescription = "Non-final token of Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but have not made the returned token `final`.
+
+ The token should be `final` in order to prevent it from being overwritten, \
+ which can cause problems when restoring the identity with \
+ `Binder.restoreCallingIdentity(token)`.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageNonFinalToken(variableName: String) = "`$variableName` is " +
+ "a non-final token from `Binder.clearCallingIdentity()`. Add `final` keyword to " +
+ "`$variableName`."
+
+ /** Issue: nested calls of Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_NESTED_CLEAR_IDENTITY_CALLS: Issue = Issue.create(
+ id = "NestedClearCallingIdentityCalls",
+ briefDescription = "Nested calls of Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()` twice without restoring identity with the \
+ result of the first call.
+
+ Make sure to restore the identity after each clear identity call.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageNestedClearIdentityCallsPrimary(
+ firstCallVariableName: String,
+ secondCallVariableName: String
+ ): String = "The calling identity has already been cleared and returned into " +
+ "`$firstCallVariableName`. Move `$secondCallVariableName` declaration after " +
+ "restoring the calling identity with " +
+ "`Binder.restoreCallingIdentity($firstCallVariableName)`."
+
+ private fun getIncidentMessageNestedClearIdentityCallsSecondary(
+ firstCallVariableName: String
+ ): String = "Location of the `$firstCallVariableName` declaration."
+
+ /** Issue: Binder.clearCallingIdentity() is not followed by `try-finally` statement */
+ @JvmField
+ val ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY: Issue = Issue.create(
+ id = "ClearIdentityCallNotFollowedByTryFinally",
+ briefDescription = "Binder.clearCallingIdentity() is not followed by try-finally " +
+ "statement",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but the next statement is not a `try` \
+ statement.
+
+ Use the following pattern for running operations with your own identity:
+
+ ```
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Code using your own identity
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ ```
+
+ Any calls/operations between `Binder.clearCallingIdentity()` and `try` \
+ statement risk throwing an exception without doing a safe and unconditional \
+ restore of the identity with `Binder.restoreCallingIdentity()` as an immediate \
+ child of the `finally` block. If you do not follow the pattern, you may run \
+ code with your identity that was originally intended to run with the calling \
+ application's identity.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageClearIdentityCallNotFollowedByTryFinally(
+ variableName: String
+ ): String = "You cleared the calling identity and returned the result into " +
+ "`$variableName`, but the next statement is not a `try`-`finally` statement. " +
+ "Define a `try`-`finally` block after `$variableName` declaration to ensure a " +
+ "safe restore of the calling identity by calling " +
+ "`Binder.restoreCallingIdentity($variableName)` and making it an immediate child " +
+ "of the `finally` block."
+
+ /** Issue: Binder.restoreCallingIdentity() is not in finally block */
+ @JvmField
+ val ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK: Issue = Issue.create(
+ id = "RestoreIdentityCallNotInFinallyBlock",
+ briefDescription = "Binder.restoreCallingIdentity() is not in finally block",
+ explanation = """
+ You are restoring the original calling identity with \
+ `Binder.restoreCallingIdentity()`, but the call is not an immediate child of \
+ the `finally` block of the `try` statement.
+
+ Use the following pattern for running operations with your own identity:
+
+ ```
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Code using your own identity
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ ```
+
+ If you do not surround the code using your identity with the `try` statement \
+ and call `Binder.restoreCallingIdentity()` as an immediate child of the \
+ `finally` block, you may run code with your identity that was originally \
+ intended to run with the calling application's identity.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageRestoreIdentityCallNotInFinallyBlock(
+ variableName: String
+ ): String = "`Binder.restoreCallingIdentity($variableName)` is not an immediate child of " +
+ "the `finally` block of the try statement after `$variableName` declaration. " +
+ "Surround the call with `finally` block and call it unconditionally."
+
+ /** Issue: Use of caller-aware methods after Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY: Issue = Issue.create(
+ id = "UseOfCallerAwareMethodsWithClearedIdentity",
+ briefDescription = "Use of caller-aware methods after " +
+ "Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but used one of the methods below before \
+ restoring the identity. These methods will use your own identity instead of \
+ the caller's identity, so if this is expected replace them with methods that \
+ explicitly query your own identity such as `Process.myUid()`, \
+ `Process.myPid()` and `UserHandle.myUserId()`, otherwise move those methods \
+ out of the `Binder.clearCallingIdentity()` / `Binder.restoreCallingIdentity()` \
+ section.
+
+ ```
+ Binder.getCallingPid()
+ Binder.getCallingUid()
+ Binder.getCallingUidOrThrow()
+ Binder.getCallingUserHandle()
+ UserHandle.getCallingAppId()
+ UserHandle.getCallingUserId()
+ ```
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
+ variableName: String,
+ methodName: String
+ ): String = "You cleared the original identity with `Binder.clearCallingIdentity()` " +
+ "and returned into `$variableName`, so `$methodName` will be using your own " +
+ "identity instead of the caller's. Either explicitly query your own identity or " +
+ "move it after restoring the identity with " +
+ "`Binder.restoreCallingIdentity($variableName)`."
+ }
+}
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt
new file mode 100644
index 000000000000..fe567da7c017
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 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.google.android.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+
+/**
+ * Lint Detector that finds issues with improper usages of the non-user getter methods of Settings
+ */
+@Suppress("UnstableApiUsage")
+class CallingSettingsNonUserGetterMethodsDetector : Detector(), SourceCodeScanner {
+ override fun getApplicableMethodNames(): List<String> = listOf(
+ "getString",
+ "getInt",
+ "getLong",
+ "getFloat"
+ )
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ val evaluator = context.evaluator
+ if (evaluator.isMemberInClass(method, "android.provider.Settings.Secure") ||
+ evaluator.isMemberInClass(method, "android.provider.Settings.System")
+ ) {
+ val message = getIncidentMessageNonUserGetterMethods(getMethodSignature(method))
+ context.report(ISSUE_NON_USER_GETTER_CALLED, node, context.getNameLocation(node),
+ message)
+ }
+ }
+
+ private fun getMethodSignature(method: PsiMethod) =
+ method.containingClass
+ ?.qualifiedName
+ ?.let { "$it#${method.name}" }
+ ?: method.name
+
+ companion object {
+ @JvmField
+ val ISSUE_NON_USER_GETTER_CALLED: Issue = Issue.create(
+ id = "NonUserGetterCalled",
+ briefDescription = "Non-ForUser Getter Method called to Settings",
+ explanation = """
+ System process should not call the non-ForUser getter methods of \
+ `Settings.Secure` or `Settings.System`. For example, instead of \
+ `Settings.Secure.getInt()`, use `Settings.Secure.getIntForUser()` instead. \
+ This will make sure that the correct Settings value is retrieved.
+ """,
+ category = Category.CORRECTNESS,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ CallingSettingsNonUserGetterMethodsDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ fun getIncidentMessageNonUserGetterMethods(methodSignature: String) =
+ "`$methodSignature()` called from system process. " +
+ "Please call `${methodSignature}ForUser()` instead. "
+ }
+}
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
new file mode 100644
index 000000000000..8011b36c9a8f
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 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.google.android.lint
+
+import com.android.tools.lint.detector.api.AnnotationInfo
+import com.android.tools.lint.detector.api.AnnotationOrigin
+import com.android.tools.lint.detector.api.AnnotationUsageInfo
+import com.android.tools.lint.detector.api.AnnotationUsageType
+import com.android.tools.lint.detector.api.ConstantEvaluator
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiAnnotation
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UElement
+
+/**
+ * Lint Detector that ensures that any method overriding a method annotated
+ * with @EnforcePermission is also annotated with the exact same annotation.
+ * The intent is to surface the effective permission checks to the service
+ * implementations.
+ */
+class EnforcePermissionDetector : Detector(), SourceCodeScanner {
+
+ val ENFORCE_PERMISSION = "android.annotation.EnforcePermission"
+
+ override fun applicableAnnotations(): List<String> {
+ return listOf(ENFORCE_PERMISSION)
+ }
+
+ private fun areAnnotationsEquivalent(
+ context: JavaContext,
+ anno1: PsiAnnotation,
+ anno2: PsiAnnotation
+ ): Boolean {
+ if (anno1.qualifiedName != anno2.qualifiedName) {
+ return false
+ }
+ val attr1 = anno1.parameterList.attributes
+ val attr2 = anno2.parameterList.attributes
+ if (attr1.size != attr2.size) {
+ return false
+ }
+ for (i in attr1.indices) {
+ if (attr1[i].name != attr2[i].name) {
+ return false
+ }
+ val v1 = ConstantEvaluator.evaluate(context, attr1[i].value)
+ val v2 = ConstantEvaluator.evaluate(context, attr2[i].value)
+ if (v1 != v2) {
+ return false
+ }
+ }
+ return true
+ }
+
+ override fun visitAnnotationUsage(
+ context: JavaContext,
+ element: UElement,
+ annotationInfo: AnnotationInfo,
+ usageInfo: AnnotationUsageInfo
+ ) {
+ if (usageInfo.type == AnnotationUsageType.EXTENDS) {
+ val newClass = element.sourcePsi?.parent?.parent as PsiClass
+ val extendedClass: PsiClass = usageInfo.referenced as PsiClass
+ val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION)
+ val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION)!!
+
+ val location = context.getLocation(element)
+ val newClassName = newClass.qualifiedName
+ val extendedClassName = extendedClass.qualifiedName
+ if (newAnnotation == null) {
+ val msg = "The class $newClassName extends the class $extendedClassName which " +
+ "is annotated with @EnforcePermission. The same annotation must be used " +
+ "on $newClassName."
+ context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+ } else if (!areAnnotationsEquivalent(context, newAnnotation, extendedAnnotation)) {
+ val msg = "The class $newClassName is annotated with ${newAnnotation.text} " +
+ "which differs from the parent class $extendedClassName: " +
+ "${extendedAnnotation.text}. The same annotation must be used for " +
+ "both classes."
+ context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+ }
+ } else if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE &&
+ annotationInfo.origin == AnnotationOrigin.METHOD) {
+ val overridingMethod = element.sourcePsi as PsiMethod
+ val overriddenMethod = usageInfo.referenced as PsiMethod
+ val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION)
+ val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION)!!
+
+ val location = context.getLocation(element)
+ val overridingClass = overridingMethod.parent as PsiClass
+ val overriddenClass = overriddenMethod.parent as PsiClass
+ val overridingName = "${overridingClass.name}.${overridingMethod.name}"
+ val overriddenName = "${overriddenClass.name}.${overriddenMethod.name}"
+ if (overridingAnnotation == null) {
+ val msg = "The method $overridingName overrides the method $overriddenName which " +
+ "is annotated with @EnforcePermission. The same annotation must be used " +
+ "on $overridingName"
+ context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+ } else if (!areAnnotationsEquivalent(
+ context, overridingAnnotation, overriddenAnnotation)) {
+ val msg = "The method $overridingName is annotated with " +
+ "${overridingAnnotation.text} which differs from the overridden " +
+ "method $overriddenName: ${overriddenAnnotation.text}. The same " +
+ "annotation must be used for both methods."
+ context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+ }
+ }
+ }
+
+ companion object {
+ val EXPLANATION = """
+ The @EnforcePermission annotation is used to indicate that the underlying binder code
+ has already verified the caller's permissions before calling the appropriate method. The
+ verification code is usually generated by the AIDL compiler, which also takes care of
+ annotating the generated Java code.
+
+ In order to surface that information to platform developers, the same annotation must be
+ used on the implementation class or methods.
+ """
+
+ val ISSUE_MISSING_ENFORCE_PERMISSION: Issue = Issue.create(
+ id = "MissingEnforcePermissionAnnotation",
+ briefDescription = "Missing @EnforcePermission annotation on Binder method",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ val ISSUE_MISMATCHING_ENFORCE_PERMISSION: Issue = Issue.create(
+ id = "MismatchingEnforcePermissionAnnotation",
+ briefDescription = "Incorrect @EnforcePermission annotation on Binder method",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+ }
+}
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt
new file mode 100644
index 000000000000..e1a5c613dee1
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2021 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.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = CallingIdentityTokenDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ CallingIdentityTokenDetector.ISSUE_UNUSED_TOKEN,
+ CallingIdentityTokenDetector.ISSUE_NON_FINAL_TOKEN,
+ CallingIdentityTokenDetector.ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+ CallingIdentityTokenDetector.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+ CallingIdentityTokenDetector.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+ CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ /** No issue scenario */
+
+ fun testDoesNotDetectIssuesInCorrectScenario() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethod() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token1);
+ }
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ final long token3 = clearCallingIdentity();
+ try {
+ } finally {
+ restoreCallingIdentity(token3);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ /** Unused token issue tests */
+
+ fun testDetectsUnusedTokens() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethodImported() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethodFullClass() {
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethodChildOfBinder() {
+ final long token3 = clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token1 has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token1) in finally or \
+ remove token1. [UnusedTokenOfOriginalCallingIdentity]
+ final long token1 = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:11: Warning: token2 has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token2) in finally or \
+ remove token2. [UnusedTokenOfOriginalCallingIdentity]
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: token3 has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token3) in finally or \
+ remove token3. [UnusedTokenOfOriginalCallingIdentity]
+ final long token3 = clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 3 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsUnusedTokensInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodTokenFromClearIdentity() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethodTokenNotFromClearIdentity() {
+ long token = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token) in finally or \
+ remove token. [UnusedTokenOfOriginalCallingIdentity]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDoesNotDetectUsedTokensInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodTokenFromClearIdentity() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ private void testMethodTokenNotFromClearIdentity() {
+ long token = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDetectsUnusedTokensWithSimilarNamesInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethod1() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethod2() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token) in finally or \
+ remove token. [UnusedTokenOfOriginalCallingIdentity]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:11: Warning: token has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token) in finally or \
+ remove token. [UnusedTokenOfOriginalCallingIdentity]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 2 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Non-final token issue tests */
+
+ fun testDetectsNonFinalTokens() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethod() {
+ long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token1);
+ }
+ long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ long token3 = clearCallingIdentity();
+ try {
+ } finally {
+ restoreCallingIdentity(token3);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token1 is a non-final token from \
+ Binder.clearCallingIdentity(). Add final keyword to token1. \
+ [NonFinalTokenOfOriginalCallingIdentity]
+ long token1 = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:10: Warning: token2 is a non-final token from \
+ Binder.clearCallingIdentity(). Add final keyword to token2. \
+ [NonFinalTokenOfOriginalCallingIdentity]
+ long token2 = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:15: Warning: token3 is a non-final token from \
+ Binder.clearCallingIdentity(). Add final keyword to token3. \
+ [NonFinalTokenOfOriginalCallingIdentity]
+ long token3 = clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 3 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Nested clearCallingIdentity() calls issue tests */
+
+ fun testDetectsNestedClearCallingIdentityCalls() {
+ // Pattern: clear - clear - clear - restore - restore - restore
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethod() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ final long token3 = clearCallingIdentity();
+ try {
+ } finally {
+ restoreCallingIdentity(token3);
+ }
+ } finally {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token1);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:7: Warning: The calling identity has already \
+ been cleared and returned into token1. Move token2 declaration after \
+ restoring the calling identity with Binder.restoreCallingIdentity(token1). \
+ [NestedClearCallingIdentityCalls]
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:5: Location of the token1 declaration.
+ src/test/pkg/TestClass1.java:9: Warning: The calling identity has already \
+ been cleared and returned into token1. Move token3 declaration after \
+ restoring the calling identity with Binder.restoreCallingIdentity(token1). \
+ [NestedClearCallingIdentityCalls]
+ final long token3 = clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:5: Location of the token1 declaration.
+ 0 errors, 2 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** clearCallingIdentity() not followed by try-finally issue tests */
+
+ fun testDetectsClearIdentityCallNotFollowedByTryFinally() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder{
+ private void testMethodNoTry() {
+ final long token = Binder.clearCallingIdentity();
+ Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodSomethingBetweenClearAndTry() {
+ final long token = Binder.clearCallingIdentity();
+ int pid = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ private void testMethodLocalVariableBetweenClearAndTry() {
+ final long token = clearCallingIdentity(), num = 0;
+ try {
+ } finally {
+ restoreCallingIdentity(token);
+ }
+ }
+ private void testMethodTryCatch() {
+ final long token = android.os.Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodTryCatchInScopes() {
+ final long token = android.os.Binder.clearCallingIdentity();
+ {
+ try {
+ } catch (Exception e) {
+ }
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:9: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = clearCallingIdentity(), num = 0;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:24: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:31: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 5 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** restoreCallingIdentity() call not in finally block issue tests */
+
+ fun testDetectsRestoreCallingIdentityCallNotInFinally() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethodImported() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ } finally {
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodFullClass() {
+ final long token = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ android.os.Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodRestoreInCatch() {
+ final long token = clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ restoreCallingIdentity(token);
+ } finally {
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:10: Warning: \
+ Binder.restoreCallingIdentity(token) is not an immediate child of the \
+ finally block of the try statement after token declaration. Surround the c\
+ all with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: \
+ Binder.restoreCallingIdentity(token) is not an immediate child of the \
+ finally block of the try statement after token declaration. Surround the c\
+ all with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ android.os.Binder.restoreCallingIdentity(token);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:23: Warning: \
+ Binder.restoreCallingIdentity(token) is not an immediate child of the \
+ finally block of the try statement after token declaration. Surround the c\
+ all with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ restoreCallingIdentity(token);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 3 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsRestoreCallingIdentityCallNotInFinallyInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethodOutsideFinally() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ } finally {
+ }
+ {
+ Binder.restoreCallingIdentity(token1);
+ }
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ {
+ {
+ {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ }
+ }
+ }
+ private void testMethodInsideFinallyInScopes() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ {
+ {
+ Binder.restoreCallingIdentity(token1);
+ }
+ }
+ }
+ final long token2 = clearCallingIdentity();
+ try {
+ } finally {
+ if (true) restoreCallingIdentity(token2);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:11: Warning: \
+ Binder.restoreCallingIdentity(token1) is not an immediate child of the \
+ finally block of the try statement after token1 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token1);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:20: Warning: \
+ Binder.restoreCallingIdentity(token2) is not an immediate child of the \
+ finally block of the try statement after token2 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ android.os.Binder.restoreCallingIdentity(token2);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:31: Warning: \
+ Binder.restoreCallingIdentity(token1) is not an immediate child of the \
+ finally block of the try statement after token1 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token1);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:38: Warning: \
+ Binder.restoreCallingIdentity(token2) is not an immediate child of the \
+ finally block of the try statement after token2 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ if (true) restoreCallingIdentity(token2);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 4 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Use of caller-aware methods after clearCallingIdentity() issue tests */
+
+ fun testDetectsUseOfCallerAwareMethodsWithClearedIdentityIssuesInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ import android.os.UserHandle;
+ public class TestClass1 {
+ private void testMethod() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int pid1 = Binder.getCallingPid();
+ int pid2 = android.os.Binder.getCallingPid();
+ int uid1 = Binder.getCallingUid();
+ int uid2 = android.os.Binder.getCallingUid();
+ int uid3 = Binder.getCallingUidOrThrow();
+ int uid4 = android.os.Binder.getCallingUidOrThrow();
+ UserHandle uh1 = Binder.getCallingUserHandle();
+ UserHandle uh2 = android.os.Binder.getCallingUserHandle();
+ {
+ int appId1 = UserHandle.getCallingAppId();
+ int appId2 = android.os.UserHandle.getCallingAppId();
+ int userId1 = UserHandle.getCallingUserId();
+ int userId2 = android.os.UserHandle.getCallingUserId();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:8: Warning: You cleared the original identity \
+ with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingPid() will be using your own identity instead of the \
+ caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int pid1 = Binder.getCallingPid();
+ ~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:9: Warning: You cleared the original identity \
+ with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingPid() will be using your own identity instead \
+ of the caller's. Either explicitly query your own identity or move it \
+ after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int pid2 = android.os.Binder.getCallingPid();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:10: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUid() will be using your own identity instead of the \
+ caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid1 = Binder.getCallingUid();
+ ~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:11: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUid() will be using your own identity instead \
+ of the caller's. Either explicitly query your own identity or move it \
+ after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid2 = android.os.Binder.getCallingUid();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:12: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUidOrThrow() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid3 = Binder.getCallingUidOrThrow();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:13: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUidOrThrow() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid4 = android.os.Binder.getCallingUidOrThrow();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:14: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUserHandle() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ UserHandle uh1 = Binder.getCallingUserHandle();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:15: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUserHandle() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ UserHandle uh2 = android.os.Binder.getCallingUserHandle();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingAppId() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int appId1 = UserHandle.getCallingAppId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:18: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingAppId() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int appId2 = android.os.UserHandle.getCallingAppId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:19: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUserId() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int userId1 = UserHandle.getCallingUserId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:20: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUserId() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int userId2 = android.os.UserHandle.getCallingUserId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 12 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Stubs for classes used for testing */
+
+ private val binderStub: TestFile = java(
+ """
+ package android.os;
+ public class Binder {
+ public static final native long clearCallingIdentity() {
+ return 0;
+ }
+ public static final native void restoreCallingIdentity(long token) {
+ }
+ public static final native int getCallingPid() {
+ return 0;
+ }
+ public static final native int getCallingUid() {
+ return 0;
+ }
+ public static final int getCallingUidOrThrow() {
+ return 0;
+ }
+ public static final @NonNull UserHandle getCallingUserHandle() {
+ return UserHandle.of(UserHandle.getUserId(getCallingUid()));
+ }
+ }
+ """
+ ).indented()
+
+ private val userHandleStub: TestFile = java(
+ """
+ package android.os;
+ import android.annotation.AppIdInt;
+ import android.annotation.UserIdInt;
+ public class UserHandle {
+ public static @AppIdInt int getCallingAppId() {
+ return getAppId(Binder.getCallingUid());
+ }
+ public static @UserIdInt int getCallingUserId() {
+ return getUserId(Binder.getCallingUid());
+ }
+ public static @UserIdInt int getUserId(int uid) {
+ return 0;
+ }
+ public static @AppIdInt int getAppId(int uid) {
+ return 0;
+ }
+ public static UserHandle of(@UserIdInt int userId) {
+ return new UserHandle();
+ }
+ }
+ """
+ ).indented()
+
+ private val userIdIntStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface UserIdInt {
+ }
+ """
+ ).indented()
+
+ private val appIdIntStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface AppIdInt {
+ }
+ """
+ ).indented()
+
+ private val stubs = arrayOf(binderStub, userHandleStub, userIdIntStub, appIdIntStub)
+
+ // Substitutes "backslash + new line" with an empty string to imitate line continuation
+ private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt
new file mode 100644
index 000000000000..e72f38416310
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 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.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class CallingSettingsNonUserGetterMethodsIssueDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = CallingSettingsNonUserGetterMethodsDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ fun testDoesNotDetectIssues() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings.Secure;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ final int value = Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY1, 0, 0);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDetectsNonUserGetterCalledFromSecure() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings.Secure;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ final int value = Secure.getInt(context.getContentResolver(),
+ Settings.Secure.KEY1);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Error: \
+ android.provider.Settings.Secure#getInt() called from system process. \
+ Please call android.provider.Settings.Secure#getIntForUser() instead. \
+ [NonUserGetterCalled]
+ final int value = Secure.getInt(context.getContentResolver(),
+ ~~~~~~
+ 1 errors, 0 warnings
+ """.addLineContinuation()
+ )
+ }
+ fun testDetectsNonUserGetterCalledFromSystem() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings.System;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ final float value = System.getFloat(context.getContentResolver(),
+ Settings.System.KEY1);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Error: \
+ android.provider.Settings.System#getFloat() called from system process. \
+ Please call android.provider.Settings.System#getFloatForUser() instead. \
+ [NonUserGetterCalled]
+ final float value = System.getFloat(context.getContentResolver(),
+ ~~~~~~~~
+ 1 errors, 0 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsNonUserGetterCalledFromSettings() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ float value = Settings.System.getFloat(context.getContentResolver(),
+ Settings.System.KEY1);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Error: \
+ android.provider.Settings.System#getFloat() called from system process. \
+ Please call android.provider.Settings.System#getFloatForUser() instead. \
+ [NonUserGetterCalled]
+ float value = Settings.System.getFloat(context.getContentResolver(),
+ ~~~~~~~~
+ 1 errors, 0 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsNonUserGettersCalledFromSystemAndSecure() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings.Secure;
+ import android.provider.Settings.System;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ final long value1 = Secure.getLong(context.getContentResolver(),
+ Settings.Secure.KEY1, 0);
+ final String value2 = System.getString(context.getContentResolver(),
+ Settings.System.KEY2);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:6: Error: \
+ android.provider.Settings.Secure#getLong() called from system process. \
+ Please call android.provider.Settings.Secure#getLongForUser() instead. \
+ [NonUserGetterCalled]
+ final long value1 = Secure.getLong(context.getContentResolver(),
+ ~~~~~~~
+ src/test/pkg/TestClass1.java:8: Error: \
+ android.provider.Settings.System#getString() called from system process. \
+ Please call android.provider.Settings.System#getStringForUser() instead. \
+ [NonUserGetterCalled]
+ final String value2 = System.getString(context.getContentResolver(),
+ ~~~~~~~~~
+ 2 errors, 0 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ private val SettingsStub: TestFile = java(
+ """
+ package android.provider;
+ public class Settings {
+ public class Secure {
+ float getFloat(ContentResolver cr, String key) {
+ return 0.0f;
+ }
+ long getLong(ContentResolver cr, String key) {
+ return 0l;
+ }
+ int getInt(ContentResolver cr, String key) {
+ return 0;
+ }
+ }
+ public class System {
+ float getFloat(ContentResolver cr, String key) {
+ return 0.0f;
+ }
+ long getLong(ContentResolver cr, String key) {
+ return 0l;
+ }
+ String getString(ContentResolver cr, String key) {
+ return null;
+ }
+ }
+ }
+ """
+ ).indented()
+
+ private val stubs = arrayOf(SettingsStub)
+
+ // Substitutes "backslash + new line" with an empty string to imitate line continuation
+ private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
new file mode 100644
index 000000000000..f5f4ebee24e0
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2022 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.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class EnforcePermissionDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = EnforcePermissionDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
+ EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ fun testDoesNotDetectIssuesCorrectAnnotationOnClass() {
+ lint().files(java(
+ """
+ package test.pkg;
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public class TestClass1 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDoesNotDetectIssuesCorrectAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ import android.annotation.EnforcePermission;
+ public class TestClass2 extends IFooMethod.Stub {
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDetectIssuesMismatchingAnnotationOnClass() {
+ lint().files(java(
+ """
+ package test.pkg;
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ public class TestClass3 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass3.java:3: Error: The class test.pkg.TestClass3 is \
+annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
+which differs from the parent class IFoo.Stub: \
+@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The \
+same annotation must be used for both classes. [MismatchingEnforcePermissionAnnotation]
+public class TestClass3 extends IFoo.Stub {
+ ~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ fun testDetectIssuesMismatchingAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass4 extends IFooMethod.Stub {
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass4.java:4: Error: The method TestClass4.testMethod is \
+annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
+which differs from the overridden method Stub.testMethod: \
+@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The same \
+annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethod() {}
+ ~~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ fun testDetectIssuesMissingAnnotationOnClass() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass5 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass5.java:2: Error: The class test.pkg.TestClass5 extends \
+the class IFoo.Stub which is annotated with @EnforcePermission. The same annotation must be \
+used on test.pkg.TestClass5. [MissingEnforcePermissionAnnotation]
+public class TestClass5 extends IFoo.Stub {
+ ~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ fun testDetectIssuesMissingAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass6 extends IFooMethod.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass6.java:3: Error: The method TestClass6.testMethod \
+overrides the method Stub.testMethod which is annotated with @EnforcePermission. The same \
+annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnotation]
+ public void testMethod() {}
+ ~~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ /* Stubs */
+
+ private val interfaceIFooStub: TestFile = java(
+ """
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public interface IFoo {
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static abstract class Stub implements IFoo {
+ @Override
+ public void testMethod() {}
+ }
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val interfaceIFooMethodStub: TestFile = java(
+ """
+ public interface IFooMethod {
+ public static abstract class Stub implements IFooMethod {
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() {}
+ }
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val manifestPermissionStub: TestFile = java(
+ """
+ package android.Manifest;
+ class permission {
+ public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+ public static final String INTERNET = "android.permission.INTERNET";
+ }
+ """
+ ).indented()
+
+ private val enforcePermissionAnnotationStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface EnforcePermission {}
+ """
+ ).indented()
+
+ private val stubs = arrayOf(interfaceIFooStub, interfaceIFooMethodStub,
+ manifestPermissionStub, enforcePermissionAnnotationStub)
+
+ // Substitutes "backslash + new line" with an empty string to imitate line continuation
+ private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}
diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp
index 98c0e69cbf8e..6efd1f64d8fe 100644
--- a/tools/locked_region_code_injection/Android.bp
+++ b/tools/locked_region_code_injection/Android.bp
@@ -12,10 +12,10 @@ java_binary_host {
manifest: "manifest.txt",
srcs: ["src/**/*.java"],
static_libs: [
- "asm-6.0",
- "asm-commons-6.0",
- "asm-tree-6.0",
- "asm-analysis-6.0",
+ "asm-9.2",
+ "asm-commons-9.2",
+ "asm-tree-9.2",
+ "asm-analysis-9.2",
"guava-21.0",
],
}
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
index 1002c88e9281..335b3f82c533 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
@@ -13,8 +13,6 @@
*/
package lockedregioncodeinjection;
-import java.util.ArrayList;
-import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
@@ -22,6 +20,9 @@ import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.BasicInterpreter;
import org.objectweb.asm.tree.analysis.BasicValue;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A simple dataflow analysis to determine if the operands on the stack must be one of target lock
* class type.
@@ -31,6 +32,7 @@ public class LockTargetStateAnalysis extends BasicInterpreter {
private final List<LockTarget> targetLocks;
public LockTargetStateAnalysis(List<LockTarget> targetLocks) {
+ super(Utils.ASM_VERSION);
this.targetLocks = targetLocks;
}
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
index 219c2b3a2fec..f1e84b1d8a33 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
@@ -13,13 +13,14 @@
*/
package lockedregioncodeinjection;
+import org.objectweb.asm.Opcodes;
+
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.Opcodes;
public class Utils {
- public static final int ASM_VERSION = Opcodes.ASM6;
+ public static final int ASM_VERSION = Opcodes.ASM7;
/**
* Reads a comma separated configuration similar to the Jack definition.
diff --git a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
index c408b9e99c32..31fa0bf63416 100644
--- a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
+++ b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
@@ -30,7 +30,7 @@ import org.junit.Test;
* rm -fr out/*
*
* # Make booster
- * javac -cp lib/asm-6.0_BETA.jar:lib/asm-commons-6.0_BETA.jar:lib/asm-tree-6.0_BETA.jar:lib/asm-analysis-6.0_BETA.jar:lib/guava-21.0.jar src&#47;*&#47;*.java -d out/
+ * javac -cp lib/asm-7.0_BETA.jar:lib/asm-commons-7.0_BETA.jar:lib/asm-tree-7.0_BETA.jar:lib/asm-analysis-7.0_BETA.jar:lib/guava-21.0.jar src&#47;*&#47;*.java -d out/
* pushd out
* jar cfe lockedregioncodeinjection.jar lockedregioncodeinjection.Main *&#47;*.class
* popd
@@ -43,7 +43,7 @@ import org.junit.Test;
* popd
*
* # Run tool on unit tests.
- * java -ea -cp lib/asm-6.0_BETA.jar:lib/asm-commons-6.0_BETA.jar:lib/asm-tree-6.0_BETA.jar:lib/asm-analysis-6.0_BETA.jar:lib/guava-21.0.jar:out/lockedregioncodeinjection.jar \
+ * java -ea -cp lib/asm-7.0_BETA.jar:lib/asm-commons-7.0_BETA.jar:lib/asm-tree-7.0_BETA.jar:lib/asm-analysis-7.0_BETA.jar:lib/guava-21.0.jar:out/lockedregioncodeinjection.jar \
* lockedregioncodeinjection.Main \
* -i out/test_input.jar -o out/test_output.jar \
* --targets 'Llockedregioncodeinjection/TestTarget;' \
diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
index 1aec9b812e61..2e60f64b21e8 100644
--- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
+++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
@@ -21,8 +21,6 @@ import com.android.codegen.BASE_BUILDER_CLASS
import com.android.codegen.CANONICAL_BUILDER_CLASS
import com.android.codegen.CODEGEN_NAME
import com.android.codegen.CODEGEN_VERSION
-import com.sun.tools.javac.code.Symbol
-import com.sun.tools.javac.code.Type
import java.io.File
import java.io.FileNotFoundException
import javax.annotation.processing.AbstractProcessor
@@ -33,6 +31,7 @@ import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.Element
import javax.lang.model.element.ElementKind
import javax.lang.model.element.TypeElement
+import javax.lang.model.type.ExecutableType
import javax.tools.Diagnostic
private const val STALE_FILE_THRESHOLD_MS = 1000
@@ -102,14 +101,13 @@ class StaleDataclassProcessor: AbstractProcessor() {
append(" ")
append(elem.annotationMirrors.joinToString(" ", transform = { annotationToString(it) }))
append(" ")
- if (elem is Symbol) {
- if (elem.type is Type.MethodType) {
- append((elem.type as Type.MethodType).returnType)
- } else {
- append(elem.type)
- }
- append(" ")
+ val type = elem.asType()
+ if (type is ExecutableType) {
+ append(type.returnType)
+ } else {
+ append(type)
}
+ append(" ")
append(elem)
}
}
@@ -234,4 +232,4 @@ class StaleDataclassProcessor: AbstractProcessor() {
override fun getSupportedSourceVersion(): SourceVersion {
return SourceVersion.latest()
}
-} \ No newline at end of file
+}
diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp
index 9d773e44faea..6ebacd8a0b14 100644
--- a/tools/sdkparcelables/Android.bp
+++ b/tools/sdkparcelables/Android.bp
@@ -14,7 +14,7 @@ java_binary_host {
"src/**/*.kt",
],
static_libs: [
- "asm-6.0",
+ "asm-9.2",
],
}
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
index 22e8d781335b..0fb062f280e3 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -39,7 +39,7 @@ fun main(args: Array<String>) {
kotlin.system.exitProcess(2)
}
- val ancestorCollector = AncestorCollector(Opcodes.ASM6, null)
+ val ancestorCollector = AncestorCollector(Opcodes.ASM7, null)
for (entry in zipFile.entries()) {
if (entry.name.endsWith(".class")) {
diff --git a/tools/split-select/Android.bp b/tools/split-select/Android.bp
index c12fc6a7f253..540265793de6 100644
--- a/tools/split-select/Android.bp
+++ b/tools/split-select/Android.bp
@@ -48,7 +48,6 @@ cc_defaults {
"libbase",
"libz",
],
- group_static_libs: true,
target: {
windows: {
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 1ec83a360048..b18bdff7263f 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -69,7 +69,6 @@ java_library {
"test/**/*.proto",
],
proto: {
- plugin: "javastream",
+ type: "stream",
},
- static_libs: ["libprotobuf-java-lite"],
}
diff --git a/tools/streaming_proto/Errors.cpp b/tools/streaming_proto/Errors.cpp
index 0cd9037dcb55..6890d99b104c 100644
--- a/tools/streaming_proto/Errors.cpp
+++ b/tools/streaming_proto/Errors.cpp
@@ -1,5 +1,6 @@
#include "Errors.h"
+#include <stdarg.h>
#include <stdlib.h>
namespace android {
diff --git a/tools/traceinjection/Android.bp b/tools/traceinjection/Android.bp
new file mode 100644
index 000000000000..39d1b1c2defd
--- /dev/null
+++ b/tools/traceinjection/Android.bp
@@ -0,0 +1,49 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_binary_host {
+ name: "traceinjection",
+ manifest: "manifest.txt",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "asm-9.2",
+ "asm-commons-9.2",
+ "asm-tree-9.2",
+ "asm-analysis-9.2",
+ "guava-21.0",
+ ],
+}
+
+java_library_host {
+ name: "TraceInjectionTests-Uninjected",
+ srcs: ["test/**/*.java"],
+ static_libs: [
+ "junit",
+ ],
+}
+
+java_genrule_host {
+ name: "TraceInjectionTests-Injected",
+ srcs: [":TraceInjectionTests-Uninjected"],
+ tools: ["traceinjection"],
+ cmd: "$(location traceinjection) " +
+ " --annotation \"com/android/traceinjection/Trace\"" +
+ " --start \"com/android/traceinjection/InjectionTests.traceStart\"" +
+ " --end \"com/android/traceinjection/InjectionTests.traceEnd\"" +
+ " -o $(out) " +
+ " -i $(in)",
+ out: ["TraceInjectionTests-Injected.jar"],
+}
+
+java_test_host {
+ name: "TraceInjectionTests",
+ static_libs: [
+ "TraceInjectionTests-Injected",
+ ],
+}
diff --git a/tools/traceinjection/manifest.txt b/tools/traceinjection/manifest.txt
new file mode 100644
index 000000000000..7f4ee1d617fa
--- /dev/null
+++ b/tools/traceinjection/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.traceinjection.Main
diff --git a/tools/traceinjection/src/com/android/traceinjection/Main.java b/tools/traceinjection/src/com/android/traceinjection/Main.java
new file mode 100644
index 000000000000..190df819dd64
--- /dev/null
+++ b/tools/traceinjection/src/com/android/traceinjection/Main.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 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.traceinjection;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.BufferedInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public class Main {
+ public static void main(String[] args) throws IOException {
+ String inJar = null;
+ String outJar = null;
+ String annotation = null;
+ String traceStart = null;
+ String traceEnd = null;
+
+ // All arguments require a value currently, so just make sure we have an even number and
+ // then process them all two at a time.
+ if (args.length % 2 != 0) {
+ throw new IllegalArgumentException("Argument is missing corresponding value");
+ }
+ for (int i = 0; i < args.length - 1; i += 2) {
+ final String arg = args[i].trim();
+ final String argValue = args[i + 1].trim();
+ if ("-i".equals(arg)) {
+ inJar = argValue;
+ } else if ("-o".equals(arg)) {
+ outJar = argValue;
+ } else if ("--annotation".equals(arg)) {
+ annotation = argValue;
+ } else if ("--start".equals(arg)) {
+ traceStart = argValue;
+ } else if ("--end".equals(arg)) {
+ traceEnd = argValue;
+ } else {
+ throw new IllegalArgumentException("Unknown argument: " + arg);
+ }
+ }
+
+ if (inJar == null) {
+ throw new IllegalArgumentException("input jar is required");
+ }
+
+ if (outJar == null) {
+ throw new IllegalArgumentException("output jar is required");
+ }
+
+ if (annotation == null) {
+ throw new IllegalArgumentException("trace annotation is required");
+ }
+
+ if (traceStart == null) {
+ throw new IllegalArgumentException("start trace method is required");
+ }
+
+ if (traceEnd == null) {
+ throw new IllegalArgumentException("end trace method is required");
+ }
+
+ TraceInjectionConfiguration params =
+ new TraceInjectionConfiguration(annotation, traceStart, traceEnd);
+
+ try (
+ ZipFile zipSrc = new ZipFile(inJar);
+ ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outJar));
+ ) {
+ Enumeration<? extends ZipEntry> srcEntries = zipSrc.entries();
+ while (srcEntries.hasMoreElements()) {
+ ZipEntry entry = srcEntries.nextElement();
+ ZipEntry newEntry = new ZipEntry(entry.getName());
+ newEntry.setTime(entry.getTime());
+ zos.putNextEntry(newEntry);
+ BufferedInputStream bis = new BufferedInputStream(zipSrc.getInputStream(entry));
+
+ if (entry.getName().endsWith(".class")) {
+ convert(bis, zos, params);
+ } else {
+ while (bis.available() > 0) {
+ zos.write(bis.read());
+ }
+ zos.closeEntry();
+ bis.close();
+ }
+ }
+ zos.finish();
+ }
+ }
+
+ private static void convert(InputStream in, OutputStream out,
+ TraceInjectionConfiguration params) throws IOException {
+ ClassReader cr = new ClassReader(in);
+ ClassWriter cw = new ClassWriter(0);
+ TraceInjectionClassVisitor cv = new TraceInjectionClassVisitor(cw, params);
+ cr.accept(cv, ClassReader.EXPAND_FRAMES);
+ byte[] data = cw.toByteArray();
+ out.write(data);
+ }
+}
diff --git a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java
new file mode 100644
index 000000000000..863f976b8aff
--- /dev/null
+++ b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.traceinjection;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * {@link ClassVisitor} that injects tracing code to methods annotated with the configured
+ * annotation.
+ */
+public class TraceInjectionClassVisitor extends ClassVisitor {
+ private final TraceInjectionConfiguration mParams;
+ public TraceInjectionClassVisitor(ClassVisitor classVisitor,
+ TraceInjectionConfiguration params) {
+ super(Opcodes.ASM7, classVisitor);
+ mParams = params;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ MethodVisitor chain = super.visitMethod(access, name, desc, signature, exceptions);
+ return new TraceInjectionMethodAdapter(chain, access, name, desc, mParams);
+ }
+}
diff --git a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionConfiguration.java b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionConfiguration.java
new file mode 100644
index 000000000000..f9595bdad9cf
--- /dev/null
+++ b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionConfiguration.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.traceinjection;
+
+/**
+ * Configuration data for trace method injection.
+ */
+public class TraceInjectionConfiguration {
+ public final String annotation;
+ public final String startMethodClass;
+ public final String startMethodName;
+ public final String endMethodClass;
+ public final String endMethodName;
+
+ public TraceInjectionConfiguration(String annotation, String startMethod, String endMethod) {
+ this.annotation = annotation;
+ String[] startMethodComponents = parseMethod(startMethod);
+ String[] endMethodComponents = parseMethod(endMethod);
+ startMethodClass = startMethodComponents[0];
+ startMethodName = startMethodComponents[1];
+ endMethodClass = endMethodComponents[0];
+ endMethodName = endMethodComponents[1];
+ }
+
+ public String toString() {
+ return "TraceInjectionParams{annotation=" + annotation
+ + ", startMethod=" + startMethodClass + "." + startMethodName
+ + ", endMethod=" + endMethodClass + "." + endMethodName + "}";
+ }
+
+ private static String[] parseMethod(String method) {
+ String[] methodComponents = method.split("\\.");
+ if (methodComponents.length != 2) {
+ throw new IllegalArgumentException("Invalid method descriptor: " + method);
+ }
+ return methodComponents;
+ }
+}
diff --git a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java
new file mode 100644
index 000000000000..c2bbddcb5668
--- /dev/null
+++ b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 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.traceinjection;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.AdviceAdapter;
+import org.objectweb.asm.commons.Method;
+
+/**
+ * Adapter that injects tracing code to methods annotated with the configured annotation.
+ *
+ * Assuming the configured annotation is {@code @Trace} and the configured methods are
+ * {@code Tracing.begin()} and {@code Tracing.end()}, it effectively transforms:
+ *
+ * <pre>{@code
+ * @Trace
+ * void method() {
+ * doStuff();
+ * }
+ * }</pre>
+ *
+ * into:
+ * <pre>{@code
+ * @Trace
+ * void method() {
+ * Tracing.begin();
+ * try {
+ * doStuff();
+ * } finally {
+ * Tracing.end();
+ * }
+ * }
+ * }</pre>
+ */
+public class TraceInjectionMethodAdapter extends AdviceAdapter {
+ private final TraceInjectionConfiguration mParams;
+ private final Label mStartFinally = newLabel();
+ private final boolean mIsConstructor;
+
+ private boolean mShouldTrace;
+ private long mTraceId;
+ private String mTraceLabel;
+
+ public TraceInjectionMethodAdapter(MethodVisitor methodVisitor, int access,
+ String name, String descriptor, TraceInjectionConfiguration params) {
+ super(Opcodes.ASM7, methodVisitor, access, name, descriptor);
+ mParams = params;
+ mIsConstructor = "<init>".equals(name);
+ }
+
+ @Override
+ public void visitCode() {
+ super.visitCode();
+ if (mShouldTrace) {
+ visitLabel(mStartFinally);
+ }
+ }
+
+ @Override
+ protected void onMethodEnter() {
+ if (!mShouldTrace) {
+ return;
+ }
+ Type type = Type.getType(toJavaSpecifier(mParams.startMethodClass));
+ Method trace = Method.getMethod("void " + mParams.startMethodName + " (long, String)");
+ push(mTraceId);
+ push(getTraceLabel());
+ invokeStatic(type, trace);
+ }
+
+ private String getTraceLabel() {
+ return !isEmpty(mTraceLabel) ? mTraceLabel : getName();
+ }
+
+ @Override
+ protected void onMethodExit(int opCode) {
+ // Any ATHROW exits will be caught as part of our exception-handling block, so putting it
+ // here would cause us to call the end trace method multiple times.
+ if (opCode != ATHROW) {
+ onFinally();
+ }
+ }
+
+ private void onFinally() {
+ if (!mShouldTrace) {
+ return;
+ }
+ Type type = Type.getType(toJavaSpecifier(mParams.endMethodClass));
+ Method trace = Method.getMethod("void " + mParams.endMethodName + " (long)");
+ push(mTraceId);
+ invokeStatic(type, trace);
+ }
+
+ @Override
+ public void visitMaxs(int maxStack, int maxLocals) {
+ final int minStackSize;
+ if (mShouldTrace) {
+ Label endFinally = newLabel();
+ visitLabel(endFinally);
+ catchException(mStartFinally, endFinally, null);
+ // The stack will always contain exactly one element: the exception we caught
+ final Object[] stack = new Object[]{ "java/lang/Throwable"};
+ // Because we use EXPAND_FRAMES, the frame type must always be F_NEW.
+ visitFrame(F_NEW, /* numLocal= */ 0, /* local= */ null, stack.length, stack);
+ onFinally();
+ // Rethrow the exception that we caught in the finally block.
+ throwException();
+
+ // Make sure we have at least enough stack space to push the trace arguments
+ // (long, String)
+ minStackSize = Type.LONG_TYPE.getSize() + Type.getType(String.class).getSize();
+ } else {
+ // We didn't inject anything, so no need for additional stack space.
+ minStackSize = 0;
+ }
+
+ super.visitMaxs(Math.max(minStackSize, maxStack), maxLocals);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ AnnotationVisitor av = super.visitAnnotation(descriptor, visible);
+ if (descriptor.equals(toJavaSpecifier(mParams.annotation))) {
+ if (mIsConstructor) {
+ // TODO: Support constructor tracing. At the moment, constructors aren't supported
+ // because you can't put an exception handler around a super() call within the
+ // constructor itself.
+ throw new IllegalStateException("Cannot trace constructors");
+ }
+ av = new TracingAnnotationVisitor(av);
+ }
+ return av;
+ }
+
+ /**
+ * An AnnotationVisitor that pulls the trace ID and label information from the configured
+ * annotation.
+ */
+ class TracingAnnotationVisitor extends AnnotationVisitor {
+
+ TracingAnnotationVisitor(AnnotationVisitor annotationVisitor) {
+ super(Opcodes.ASM7, annotationVisitor);
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ if ("tag".equals(name)) {
+ mTraceId = (long) value;
+ // If we have a trace annotation and ID, then we have everything we need to trace
+ mShouldTrace = true;
+ } else if ("label".equals(name)) {
+ mTraceLabel = (String) value;
+ }
+ super.visit(name, value);
+ }
+ }
+
+ private static String toJavaSpecifier(String klass) {
+ return "L" + klass + ";";
+ }
+
+ private static boolean isEmpty(String str) {
+ return str == null || "".equals(str);
+ }
+}
diff --git a/tools/traceinjection/test/com/android/traceinjection/InjectionTests.java b/tools/traceinjection/test/com/android/traceinjection/InjectionTests.java
new file mode 100644
index 000000000000..81bf235fe0a6
--- /dev/null
+++ b/tools/traceinjection/test/com/android/traceinjection/InjectionTests.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 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.traceinjection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(JUnit4.class)
+public class InjectionTests {
+ public static final int TRACE_TAG = 42;
+ public static final String CUSTOM_TRACE_NAME = "Custom";
+
+ public static final TraceTracker TRACKER = new TraceTracker();
+
+ @After
+ public void tearDown() {
+ TRACKER.reset();
+ }
+
+ @Test
+ public void testDefaultLabel() {
+ assertTraces(this::tracedMethod, "tracedMethod");
+ tracedMethodThrowsAndCatches();
+ }
+
+ @Test
+ public void testCustomLabel() {
+ assertTraces(this::tracedMethodHasCustomName, CUSTOM_TRACE_NAME);
+ }
+
+ @Test
+ public void testTracedMethodsStillThrow() {
+ assertTraces(() -> assertThrows(IllegalArgumentException.class, this::tracedMethodThrows),
+ "tracedMethodThrows");
+ // Also test that we rethrow exceptions from method calls. This is slightly different from
+ // the previous case because the ATHROW instruction is not actually present at all in the
+ // bytecode of the instrumented method.
+ TRACKER.reset();
+ assertTraces(() -> assertThrows(NullPointerException.class,
+ this::tracedMethodCallsThrowingMethod),
+ "tracedMethodCallsThrowingMethod");
+ }
+
+ @Test
+ public void testNestedTracedMethods() {
+ assertTraces(this::outerTracedMethod, "outerTracedMethod", "innerTracedMethod");
+ }
+
+ @Test
+ public void testTracedMethodWithCatchBlock() {
+ assertTraces(this::tracedMethodThrowsAndCatches, "tracedMethodThrowsAndCatches");
+ }
+
+ @Test
+ public void testTracedMethodWithFinallyBlock() {
+ assertTraces(() -> assertThrows(IllegalArgumentException.class,
+ this::tracedMethodThrowWithFinally), "tracedMethodThrowWithFinally");
+ }
+
+ @Test
+ public void testNonVoidMethod() {
+ assertTraces(this::tracedNonVoidMethod, "tracedNonVoidMethod");
+ }
+
+ @Test
+ public void testNonVoidMethodReturnsWithinCatches() {
+ assertTraces(this::tracedNonVoidMethodReturnsWithinCatches,
+ "tracedNonVoidMethodReturnsWithinCatches");
+ }
+
+ @Test
+ public void testNonVoidMethodReturnsWithinFinally() {
+ assertTraces(this::tracedNonVoidMethodReturnsWithinFinally,
+ "tracedNonVoidMethodReturnsWithinFinally");
+ }
+
+ @Test
+ public void testTracedStaticMethod() {
+ assertTraces(InjectionTests::tracedStaticMethod, "tracedStaticMethod");
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethod() {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethodThrows() {
+ throw new IllegalArgumentException();
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethodCallsThrowingMethod() {
+ throwingMethod();
+ }
+
+ private void throwingMethod() {
+ throw new NullPointerException();
+ }
+
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethodThrowsAndCatches() {
+ try {
+ throw new IllegalArgumentException();
+ } catch (IllegalArgumentException ignored) {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void tracedMethodThrowWithFinally() {
+ try {
+ throw new IllegalArgumentException();
+ } finally {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+ }
+
+ @Trace(tag = TRACE_TAG, label = CUSTOM_TRACE_NAME)
+ public void tracedMethodHasCustomName() {
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void outerTracedMethod() {
+ innerTracedMethod();
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public void innerTracedMethod() {
+ assertEquals(2, TRACKER.getTraceCount(TRACE_TAG));
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public int tracedNonVoidMethod() {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ return 0;
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public int tracedNonVoidMethodReturnsWithinCatches() {
+ try {
+ throw new IllegalArgumentException();
+ } catch (IllegalArgumentException ignored) {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ return 0;
+ }
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public int tracedNonVoidMethodReturnsWithinFinally() {
+ try {
+ throw new IllegalArgumentException();
+ } finally {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ return 0;
+ }
+ }
+
+ @Trace(tag = TRACE_TAG)
+ public static void tracedStaticMethod() {
+ assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+ }
+
+ public void assertTraces(Runnable r, String... traceLabels) {
+ r.run();
+ assertEquals(Arrays.asList(traceLabels), TRACKER.getTraceLabels(TRACE_TAG));
+ TRACKER.assertAllTracesClosed();
+ }
+
+ public static void traceStart(long tag, String name) {
+ TRACKER.onTraceStart(tag, name);
+ }
+
+ public static void traceEnd(long tag) {
+ TRACKER.onTraceEnd(tag);
+ }
+
+ static class TraceTracker {
+ private final Map<Long, List<String>> mTraceLabelsByTag = new HashMap<>();
+ private final Map<Long, Integer> mTraceCountsByTag = new HashMap<>();
+
+ public void onTraceStart(long tag, String name) {
+ getTraceLabels(tag).add(name);
+ mTraceCountsByTag.put(tag, mTraceCountsByTag.getOrDefault(tag, 0) + 1);
+ }
+
+ public void onTraceEnd(long tag) {
+ final int newCount = getTraceCount(tag) - 1;
+ if (newCount < 0) {
+ throw new IllegalStateException("Trace count has gone negative for tag " + tag);
+ }
+ mTraceCountsByTag.put(tag, newCount);
+ }
+
+ public void reset() {
+ mTraceLabelsByTag.clear();
+ mTraceCountsByTag.clear();
+ }
+
+ public List<String> getTraceLabels(long tag) {
+ if (!mTraceLabelsByTag.containsKey(tag)) {
+ mTraceLabelsByTag.put(tag, new ArrayList<>());
+ }
+ return mTraceLabelsByTag.get(tag);
+ }
+
+ public int getTraceCount(long tag) {
+ return mTraceCountsByTag.getOrDefault(tag, 0);
+ }
+
+ public void assertAllTracesClosed() {
+ for (Map.Entry<Long, Integer> count: mTraceCountsByTag.entrySet()) {
+ final String errorMsg = "Tag " + count.getKey() + " is not fully closed (count="
+ + count.getValue() + ")";
+ assertEquals(errorMsg, 0, (int) count.getValue());
+ }
+ }
+ }
+}
diff --git a/tools/traceinjection/test/com/android/traceinjection/Trace.java b/tools/traceinjection/test/com/android/traceinjection/Trace.java
new file mode 100644
index 000000000000..9e1c545673e8
--- /dev/null
+++ b/tools/traceinjection/test/com/android/traceinjection/Trace.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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.traceinjection;
+
+public @interface Trace {
+ long tag();
+ String label() default "";
+}
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index 0423b7abd685..ff24d160b917 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -32,7 +32,7 @@ cc_binary_host {
"libui-types",
],
target: {
- linux_glibc: {
+ host_linux: {
static_libs: [
// libbinder is only available for linux
"libbinder",
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 991b28071515..817effd24a2d 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -29,6 +29,17 @@ using namespace android;
static const char* PROG_NAME = "validatekeymaps";
static bool gQuiet = false;
+/**
+ * Return true if 'str' contains 'substr', ignoring case.
+ */
+static bool containsSubstringCaseInsensitive(std::string_view str, std::string_view substr) {
+ auto it = std::search(str.begin(), str.end(), substr.begin(), substr.end(),
+ [](char left, char right) {
+ return std::tolower(left) == std::tolower(right);
+ });
+ return it != str.end();
+}
+
enum class FileType {
UNKNOWN,
KEY_LAYOUT,
@@ -85,6 +96,36 @@ static FileType getFileType(const char* filename) {
return FileType::UNKNOWN;
}
+/**
+ * Return true if the filename is allowed, false otherwise.
+ */
+static bool validateKeyLayoutFileName(const std::string& filename) {
+ static const std::string kMicrosoftReason =
+ "Microsoft's controllers are designed to work with Generic.kl. Please check with "
+ "Microsoft prior to adding these layouts. See b/194334400";
+ static const std::vector<std::pair<std::string, std::string>> kBannedDevices{
+ std::make_pair("Vendor_0a5c_Product_8502",
+ "This vendorId/productId combination conflicts with 'SnakeByte "
+ "iDroid:con', 'BT23BK keyboard', and other keyboards. Instead, consider "
+ "matching these specific devices by name. See b/36976285, b/191720859"),
+ std::make_pair("Vendor_045e_Product_0b05", kMicrosoftReason),
+ std::make_pair("Vendor_045e_Product_0b20", kMicrosoftReason),
+ std::make_pair("Vendor_045e_Product_0b21", kMicrosoftReason),
+ std::make_pair("Vendor_045e_Product_0b22", kMicrosoftReason),
+ };
+
+ for (const auto& [filenameSubstr, reason] : kBannedDevices) {
+ if (containsSubstringCaseInsensitive(filename, filenameSubstr)) {
+ error("You are trying to add a key layout %s, which matches %s. ", filename.c_str(),
+ filenameSubstr.c_str());
+ error("This would cause some devices to function incorrectly. ");
+ error("%s. ", reason.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
static bool validateFile(const char* filename) {
log("Validating file '%s'...\n", filename);
@@ -95,6 +136,9 @@ static bool validateFile(const char* filename) {
return false;
case FileType::KEY_LAYOUT: {
+ if (!validateKeyLayoutFileName(filename)) {
+ return false;
+ }
base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(filename);
if (!ret.ok()) {
error("Error %s parsing key layout file.\n\n", ret.error().message().c_str());