diff options
Diffstat (limited to 'tools')
51 files changed, 1247 insertions, 88 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 4c8193990feb..3f9016ba4852 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -55,7 +55,10 @@ cc_defaults { cflags: ["-D_DARWIN_UNLIMITED_STREAMS"], }, }, - header_libs: ["jni_headers"], + header_libs: [ + "jni_headers", + "native_headers", + ], static_libs: [ "libandroidfw", "libutils", diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h index 7ba3277d2093..a274f047586c 100644 --- a/tools/aapt2/Resource.h +++ b/tools/aapt2/Resource.h @@ -69,6 +69,8 @@ enum class ResourceType { kXml, }; +enum class FlagStatus { NoFlag = 0, Disabled = 1, Enabled = 2 }; + android::StringPiece to_string(ResourceType type); /** diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 6af39b739e9b..9444dd968f5f 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "ResourceParser.h" #include <functional> @@ -108,6 +107,7 @@ struct ParsedResource { Visibility::Level visibility_level = Visibility::Level::kUndefined; bool staged_api = false; bool allow_new = false; + FlagStatus flag_status = FlagStatus::NoFlag; std::optional<OverlayableItem> overlayable_item; std::optional<StagedId> staged_alias; @@ -161,6 +161,8 @@ static bool AddResourcesToTable(ResourceTable* table, android::IDiagnostics* dia res_builder.SetStagedId(res->staged_alias.value()); } + res_builder.SetFlagStatus(res->flag_status); + bool error = false; if (!res->name.entry.empty()) { if (!table->AddResource(res_builder.Build(), diag)) { @@ -544,6 +546,30 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, }); std::string resource_type = parser->element_name(); + std::optional<StringPiece> flag = + xml::FindAttribute(parser, "http://schemas.android.com/apk/res/android", "featureFlag"); + out_resource->flag_status = FlagStatus::NoFlag; + if (flag) { + auto flag_it = options_.feature_flag_values.find(flag.value()); + if (flag_it == options_.feature_flag_values.end()) { + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) + << "Resource flag value undefined"); + return false; + } + const auto& flag_properties = flag_it->second; + if (!flag_properties.read_only) { + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) + << "Only read only flags may be used with resources"); + return false; + } + if (!flag_properties.enabled.has_value()) { + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) + << "Only flags with a value may be used with resources"); + return false; + } + out_resource->flag_status = + flag_properties.enabled.value() ? FlagStatus::Enabled : FlagStatus::Disabled; + } // The value format accepted for this resource. uint32_t resource_format = 0u; @@ -664,7 +690,9 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, resource_format = item_iter->second.format; } - if (!ParseItem(parser, out_resource, resource_format)) { + // Don't bother parsing the item if it is behind a disabled flag + if (out_resource->flag_status != FlagStatus::Disabled && + !ParseItem(parser, out_resource, resource_format)) { return false; } return true; diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h index 012a056dccf3..45d41c193cb4 100644 --- a/tools/aapt2/ResourceParser.h +++ b/tools/aapt2/ResourceParser.h @@ -27,6 +27,7 @@ #include "androidfw/IDiagnostics.h" #include "androidfw/StringPiece.h" #include "androidfw/StringPool.h" +#include "cmd/Util.h" #include "xml/XmlPullParser.h" namespace aapt { @@ -54,6 +55,8 @@ 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. std::optional<Visibility::Level> visibility; + + FeatureFlagValues feature_flag_values; }; struct FlattenedXmlSubTree { diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index b59b16574c42..2e6ad13d99de 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -69,8 +69,13 @@ class ResourceParserTest : public ::testing::Test { return TestParse(str, ConfigDescription{}); } - ::testing::AssertionResult TestParse(StringPiece str, const ConfigDescription& config) { - ResourceParserOptions parserOptions; + ::testing::AssertionResult TestParse(StringPiece str, ResourceParserOptions parserOptions) { + return TestParse(str, ConfigDescription{}, parserOptions); + } + + ::testing::AssertionResult TestParse( + StringPiece str, const ConfigDescription& config, + ResourceParserOptions parserOptions = ResourceParserOptions()) { ResourceParser parser(context_->GetDiagnostics(), &table_, android::Source{"test"}, config, parserOptions); @@ -242,6 +247,19 @@ TEST_F(ResourceParserTest, ParseStringTranslatableAttribute) { EXPECT_FALSE(TestParse(R"(<string name="foo4" translatable="yes">Translate</string>)")); } +TEST_F(ResourceParserTest, ParseStringBehindDisabledFlag) { + FeatureFlagProperties flag_properties(true, false); + ResourceParserOptions options; + options.feature_flag_values = {{"falseFlag", flag_properties}}; + ASSERT_TRUE(TestParse( + R"(<string name="foo" android:featureFlag="falseFlag" + xmlns:android="http://schemas.android.com/apk/res/android">foo</string>)", + options)); + + String* str = test::GetValue<String>(&table_, "string/foo"); + ASSERT_THAT(str, IsNull()); +} + TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) { std::string input = R"( <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index a3b0b45df5c3..1cdb71551d5d 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -231,6 +231,47 @@ bool ResourceEntry::HasDefaultValue() const { return false; } +ResourceTable::CollisionResult ResourceTable::ResolveFlagCollision(FlagStatus existing, + FlagStatus incoming) { + switch (existing) { + case FlagStatus::NoFlag: + switch (incoming) { + case FlagStatus::NoFlag: + return CollisionResult::kConflict; + case FlagStatus::Disabled: + return CollisionResult::kKeepOriginal; + case FlagStatus::Enabled: + return CollisionResult::kTakeNew; + default: + return CollisionResult::kConflict; + } + case FlagStatus::Disabled: + switch (incoming) { + case FlagStatus::NoFlag: + return CollisionResult::kTakeNew; + case FlagStatus::Disabled: + return CollisionResult::kKeepOriginal; + case FlagStatus::Enabled: + return CollisionResult::kTakeNew; + default: + return CollisionResult::kConflict; + } + case FlagStatus::Enabled: + switch (incoming) { + case FlagStatus::NoFlag: + return CollisionResult::kKeepOriginal; + case FlagStatus::Disabled: + return CollisionResult::kKeepOriginal; + case FlagStatus::Enabled: + return CollisionResult::kConflict; + default: + return CollisionResult::kConflict; + } + default: + return CollisionResult::kConflict; + } +} + // The default handler for collisions. // // Typically, a weak value will be overridden by a strong value. An existing weak @@ -564,16 +605,21 @@ bool ResourceTable::AddResource(NewResource&& res, android::IDiagnostics* diag) if (!config_value->value) { // Resource does not exist, add it now. config_value->value = std::move(res.value); + config_value->flag_status = res.flag_status; } else { // When validation is enabled, ensure that a resource cannot have multiple values defined for - // the same configuration. - auto result = validate ? ResolveValueCollision(config_value->value.get(), res.value.get()) + // the same configuration unless protected by flags. + auto result = validate ? ResolveFlagCollision(config_value->flag_status, res.flag_status) : CollisionResult::kKeepBoth; + if (result == CollisionResult::kConflict) { + result = ResolveValueCollision(config_value->value.get(), res.value.get()); + } switch (result) { case CollisionResult::kKeepBoth: // Insert the value ignoring for duplicate configurations entry->values.push_back(util::make_unique<ResourceConfigValue>(res.config, res.product)); entry->values.back()->value = std::move(res.value); + entry->values.back()->flag_status = res.flag_status; break; case CollisionResult::kTakeNew: @@ -735,6 +781,11 @@ NewResourceBuilder& NewResourceBuilder::SetAllowMangled(bool allow_mangled) { return *this; } +NewResourceBuilder& NewResourceBuilder::SetFlagStatus(FlagStatus flag_status) { + res_.flag_status = flag_status; + return *this; +} + NewResource NewResourceBuilder::Build() { return std::move(res_); } diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 61e399c7ab68..4f76e7d3a2b7 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -104,6 +104,8 @@ class ResourceConfigValue { // The actual Value. std::unique_ptr<Value> value; + FlagStatus flag_status = FlagStatus::NoFlag; + ResourceConfigValue(const android::ConfigDescription& config, android::StringPiece product) : config(config), product(product) { } @@ -269,6 +271,7 @@ struct NewResource { std::optional<AllowNew> allow_new; std::optional<StagedId> staged_id; bool allow_mangled = false; + FlagStatus flag_status = FlagStatus::NoFlag; }; struct NewResourceBuilder { @@ -282,6 +285,7 @@ struct NewResourceBuilder { NewResourceBuilder& SetAllowNew(AllowNew allow_new); NewResourceBuilder& SetStagedId(StagedId id); NewResourceBuilder& SetAllowMangled(bool allow_mangled); + NewResourceBuilder& SetFlagStatus(FlagStatus flag_status); NewResource Build(); private: @@ -330,7 +334,8 @@ class ResourceTable { std::unique_ptr<ResourceTable> Clone() const; - // When a collision of resources occurs, this method decides which value to keep. + // When a collision of resources occurs, these methods decide which value to keep. + static CollisionResult ResolveFlagCollision(FlagStatus existing, FlagStatus incoming); static CollisionResult ResolveValueCollision(Value* existing, Value* incoming); // The string pool used by this resource table. Values that reference strings must use diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto index 1d7fd1d17dcd..2ecc82ae4792 100644 --- a/tools/aapt2/Resources.proto +++ b/tools/aapt2/Resources.proto @@ -246,6 +246,7 @@ message Entry { message ConfigValue { Configuration config = 1; Value value = 2; + uint32 flag_status = 3; } // The generic meta-data for every value in a resource table. diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp index 83f2eb31aa57..37b1687fd3f1 100644 --- a/tools/aapt2/SdkConstants.cpp +++ b/tools/aapt2/SdkConstants.cpp @@ -64,6 +64,12 @@ static constexpr std::pair<uint16_t, ApiVersion> sAttrIdMap[] = { {0x0616, SDK_R}, {0x064b, SDK_S}, {0x064c, SDK_S_V2}, + // TODO(zyy): add these when we need more rules for converting new attributes to the + // older ones, but don't bother for now as this would increase the array size for no + // real benefit + // {0x0672, SDK_TIRAMISU}, + // {0x0687, SDK_UPSIDE_DOWN_CAKE}, + // {0x06a3, SDK_VANILLA_ICE_CREAM}, }; static_assert(std::is_sorted(std::begin(sAttrIdMap), std::end(sAttrIdMap), diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index 9b8c3b3d549c..2a978a5153ca 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -171,6 +171,7 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options, parser_options.error_on_positional_arguments = !options.legacy_mode; parser_options.preserve_visibility_of_styleables = options.preserve_visibility_of_styleables; parser_options.translatable = translatable_file; + parser_options.feature_flag_values = options.feature_flag_values; // 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. diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp index e839fc1ceb0f..7739171b347f 100644 --- a/tools/aapt2/cmd/Util.cpp +++ b/tools/aapt2/cmd/Util.cpp @@ -137,22 +137,25 @@ bool ParseFeatureFlagsParameter(StringPiece arg, android::IDiagnostics* diag, diag->Error(android::DiagMessage() << "No name given for one or more flags in: " << arg); return false; } + std::vector<std::string> name_parts = util::Split(flag_name, ':'); if (name_parts.size() > 2) { diag->Error(android::DiagMessage() << "Invalid feature flag and optional value '" << flag_and_value - << "'. Must be in the format 'flag_name[:ro][=true|false]"); + << "'. Must be in the format 'flag_name[:READ_ONLY|READ_WRITE][=true|false]"); return false; } flag_name = name_parts[0]; bool read_only = false; if (name_parts.size() == 2) { - if (name_parts[1] == "ro") { + if (name_parts[1] == "ro" || name_parts[1] == "READ_ONLY") { read_only = true; + } else if (name_parts[1] == "READ_WRITE") { + read_only = false; } else { diag->Error(android::DiagMessage() << "Invalid feature flag and optional value '" << flag_and_value - << "'. Must be in the format 'flag_name[:ro][=true|false]"); + << "'. Must be in the format 'flag_name[:READ_ONLY|READ_WRITE][=true|false]"); return false; } } diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp index 35bc63714e58..78183409ad8f 100644 --- a/tools/aapt2/cmd/Util_test.cpp +++ b/tools/aapt2/cmd/Util_test.cpp @@ -383,7 +383,7 @@ TEST(UtilTest, ParseFeatureFlagsParameter_InvalidValue) { TEST(UtilTest, ParseFeatureFlagsParameter_DuplicateFlag) { auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics(); FeatureFlagValues feature_flag_values; - ASSERT_TRUE(ParseFeatureFlagsParameter("foo=true,bar=true,foo:ro=false", diagnostics, + ASSERT_TRUE(ParseFeatureFlagsParameter("foo=true,bar:READ_WRITE=true,foo:ro=false", diagnostics, &feature_flag_values)); EXPECT_THAT( feature_flag_values, @@ -394,11 +394,11 @@ TEST(UtilTest, ParseFeatureFlagsParameter_DuplicateFlag) { TEST(UtilTest, ParseFeatureFlagsParameter_Valid) { auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics(); FeatureFlagValues feature_flag_values; - ASSERT_TRUE(ParseFeatureFlagsParameter("foo= true, bar:ro =FALSE,baz=, quux", diagnostics, - &feature_flag_values)); + ASSERT_TRUE(ParseFeatureFlagsParameter("foo:READ_ONLY= true, bar:ro =FALSE,baz:READ_WRITE=, quux", + diagnostics, &feature_flag_values)); EXPECT_THAT( feature_flag_values, - UnorderedElementsAre(Pair("foo", FeatureFlagProperties{false, std::optional<bool>(true)}), + UnorderedElementsAre(Pair("foo", FeatureFlagProperties{true, std::optional<bool>(true)}), Pair("bar", FeatureFlagProperties{true, std::optional<bool>(false)}), Pair("baz", FeatureFlagProperties{false, std::nullopt}), Pair("quux", FeatureFlagProperties{false, std::nullopt}))); diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index e1a3013c07bb..aaab3158f61e 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -16,6 +16,7 @@ #include "format/proto/ProtoDeserialize.h" +#include "Resource.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" @@ -533,6 +534,8 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr return false; } + config_value->flag_status = (FlagStatus)pb_config_value.flag_status(); + config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config, &out_table->string_pool, files, out_error); if (config_value->value == nullptr) { diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp index 0903205b5eb2..c1e15bcf9f70 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -426,6 +426,7 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table pb_config_value->mutable_config()->set_product(config_value->product); SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(), source_pool.get()); + pb_config_value->set_flag_status((uint32_t)config_value->flag_status); } } } diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index cbf2c2fe8a9c..382b088e4bba 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -198,6 +198,7 @@ static bool UpdateConfigChangesIfNeeded(xml::Element* el, IAaptContext* context) android::DiagMessage(el->line_number) << "Cannot find symbol for android:configChanges with min sdk: " << context->GetMinSdkVersion()); + return false; } std::stringstream new_value; diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index 67a48283e8b6..1942fc11c32e 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -212,7 +212,11 @@ static ResourceTable::CollisionResult MergeConfigValue( collision_result = ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool); } else { - collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value); + collision_result = ResourceTable::ResolveFlagCollision(dst_config_value->flag_status, + src_config_value->flag_status); + if (collision_result == CollisionResult::kConflict) { + collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value); + } } if (collision_result == CollisionResult::kConflict) { @@ -291,6 +295,7 @@ bool TableMerger::DoMerge(const android::Source& src, ResourceTablePackage* src_ } else { dst_config_value = dst_entry->FindOrCreateValue(src_config_value->config, src_config_value->product); + dst_config_value->flag_status = src_config_value->flag_status; } // Continue if we're taking the new resource. diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp index 8abc26d67c1f..1527d68a6c3b 100644 --- a/tools/aapt2/xml/XmlPullParser.cpp +++ b/tools/aapt2/xml/XmlPullParser.cpp @@ -309,7 +309,14 @@ void XMLCALL XmlPullParser::EndCdataSectionHandler(void* user_data) { } std::optional<StringPiece> FindAttribute(const XmlPullParser* parser, StringPiece name) { - auto iter = parser->FindAttribute("", name); + return FindAttribute(parser, "", name); +} + +std::optional<android::StringPiece> FindAttribute(const XmlPullParser* parser, + android::StringPiece namespace_uri, + android::StringPiece name) { + auto iter = parser->FindAttribute(namespace_uri, name); + if (iter != parser->end_attributes()) { return StringPiece(util::TrimWhitespace(iter->value)); } diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h index 64274d032c61..d65ba6fb56e3 100644 --- a/tools/aapt2/xml/XmlPullParser.h +++ b/tools/aapt2/xml/XmlPullParser.h @@ -194,6 +194,13 @@ std::optional<android::StringPiece> FindAttribute(const XmlPullParser* parser, android::StringPiece name); /** + * Finds the attribute in the current element within the given namespace. + */ +std::optional<android::StringPiece> FindAttribute(const XmlPullParser* parser, + android::StringPiece namespace_uri, + android::StringPiece name); + +/** * Finds the attribute in the current element within the global namespace. The * attribute's value * must not be the empty string. diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java index 277a508ced57..5ecf5cf0b723 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java @@ -115,6 +115,9 @@ public class AppInfoFactory implements AslMarshallableFactory<AppInfo> { /** Creates a {@link AppInfo} from the human-readable DOM element. */ public AppInfo createFromHrElement(Element appInfoEle, long version) throws MalformedXmlException { + if (appInfoEle == null) { + return null; + } XmlUtils.throwIfExtraneousAttributes( appInfoEle, XmlUtils.getMostRecentVersion(mRecognizedHrAttrs, version)); XmlUtils.throwIfExtraneousChildrenHr( @@ -184,6 +187,9 @@ public class AppInfoFactory implements AslMarshallableFactory<AppInfo> { /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */ public AppInfo createFromOdElement(Element appInfoEle, long version) throws MalformedXmlException { + if (appInfoEle == null) { + return null; + } XmlUtils.throwIfExtraneousChildrenOd( appInfoEle, XmlUtils.getMostRecentVersion(mRecognizedOdEleNames, version)); var requiredOdEles = XmlUtils.getMostRecentVersion(mRequiredOdEles, version); diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java index 14e65e5e5b2b..e3aa50a4cee2 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java @@ -20,8 +20,11 @@ import com.android.asllib.marshallable.AndroidSafetyLabelTest; import com.android.asllib.marshallable.AppInfoTest; import com.android.asllib.marshallable.DataLabelsTest; import com.android.asllib.marshallable.DataTypeEqualityTest; +import com.android.asllib.marshallable.DeveloperInfoTest; import com.android.asllib.marshallable.SafetyLabelsTest; +import com.android.asllib.marshallable.SecurityLabelsTest; import com.android.asllib.marshallable.SystemAppSafetyLabelTest; +import com.android.asllib.marshallable.ThirdPartyVerificationTest; import com.android.asllib.marshallable.TransparencyInfoTest; import org.junit.runner.RunWith; @@ -36,6 +39,9 @@ import org.junit.runners.Suite; DataTypeEqualityTest.class, SafetyLabelsTest.class, SystemAppSafetyLabelTest.class, - TransparencyInfoTest.class + TransparencyInfoTest.class, + DeveloperInfoTest.class, + SecurityLabelsTest.class, + ThirdPartyVerificationTest.class }) public class AllTests {} diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java index 283ccbc44791..6470c060af87 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AndroidSafetyLabelTest.java @@ -28,6 +28,7 @@ import org.junit.runners.JUnit4; import org.w3c.dom.Element; import java.nio.file.Paths; +import java.util.List; @RunWith(JUnit4.class) public class AndroidSafetyLabelTest { @@ -37,12 +38,16 @@ public class AndroidSafetyLabelTest { "com/android/asllib/androidsafetylabel/od"; private static final String MISSING_VERSION_FILE_NAME = "missing-version.xml"; - private static final String VALID_EMPTY_FILE_NAME = "valid-empty.xml"; + private static final String VALID_V2_FILE_NAME = "valid-empty.xml"; + private static final String VALID_V1_FILE_NAME = "valid-v1.xml"; private static final String WITH_SAFETY_LABELS_FILE_NAME = "with-safety-labels.xml"; private static final String WITH_SYSTEM_APP_SAFETY_LABEL_FILE_NAME = "with-system-app-safety-label.xml"; private static final String WITH_TRANSPARENCY_INFO_FILE_NAME = "with-transparency-info.xml"; + public static final List<String> REQUIRED_FIELD_NAMES_OD_V2 = + List.of("system_app_safety_label", "transparency_info"); + @Before public void setUp() throws Exception { System.out.println("set up."); @@ -56,12 +61,12 @@ public class AndroidSafetyLabelTest { odToHrExpectException(MISSING_VERSION_FILE_NAME); } - /** Test for android safety label valid empty. */ + /** Test for android safety label valid v2. */ @Test - public void testAndroidSafetyLabelValidEmptyFile() throws Exception { - System.out.println("starting testAndroidSafetyLabelValidEmptyFile."); - testHrToOdAndroidSafetyLabel(VALID_EMPTY_FILE_NAME); - testOdToHrAndroidSafetyLabel(VALID_EMPTY_FILE_NAME); + public void testAndroidSafetyLabelValidV2File() throws Exception { + System.out.println("starting testAndroidSafetyLabelValidV2File."); + testHrToOdAndroidSafetyLabel(VALID_V2_FILE_NAME); + testOdToHrAndroidSafetyLabel(VALID_V2_FILE_NAME); } /** Test for android safety label with safety labels. */ @@ -72,6 +77,34 @@ public class AndroidSafetyLabelTest { testOdToHrAndroidSafetyLabel(WITH_SAFETY_LABELS_FILE_NAME); } + /** Tests missing required fields fails, V2. */ + @Test + public void testMissingRequiredFieldsOdV2() throws Exception { + for (String reqField : REQUIRED_FIELD_NAMES_OD_V2) { + System.out.println("testing missing required field od v2: " + reqField); + var ele = + TestUtils.getElementFromResource( + Paths.get(ANDROID_SAFETY_LABEL_OD_PATH, VALID_V2_FILE_NAME)); + TestUtils.removeOdChildEleWithName(ele, reqField); + assertThrows( + MalformedXmlException.class, + () -> new AndroidSafetyLabelFactory().createFromOdElement(ele)); + } + } + + /** Tests missing optional fields succeeds, V1. */ + @Test + public void testMissingOptionalFieldsOdV1() throws Exception { + for (String reqField : REQUIRED_FIELD_NAMES_OD_V2) { + System.out.println("testing missing optional field od v1: " + reqField); + var ele = + TestUtils.getElementFromResource( + Paths.get(ANDROID_SAFETY_LABEL_OD_PATH, VALID_V1_FILE_NAME)); + TestUtils.removeOdChildEleWithName(ele, reqField); + var unused = new AndroidSafetyLabelFactory().createFromOdElement(ele); + } + } + private void hrToOdExpectException(String fileName) { assertThrows( MalformedXmlException.class, diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java index 7806061f00bb..b4a7663a1839 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java @@ -86,11 +86,12 @@ public class AppInfoTest { @Test public void testAllFieldsValidV1() throws Exception { System.out.println("starting testAllFieldsValidV1."); - new AppInfoFactory() - .createFromOdElement( - TestUtils.getElementFromResource( - Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_V1_FILE_NAME)), - 1L); + var unused = + new AppInfoFactory() + .createFromOdElement( + TestUtils.getElementFromResource( + Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_V1_FILE_NAME)), + 1L); } /** Test for unrecognized field v1. */ @@ -133,7 +134,7 @@ public class AppInfoTest { TestUtils.getElementFromResource( Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_V1_FILE_NAME)); TestUtils.removeOdChildEleWithName(appInfoEle, optField); - new AppInfoFactory().createFromOdElement(appInfoEle, 1L); + var unused = new AppInfoFactory().createFromOdElement(appInfoEle, 1L); } } @@ -202,7 +203,7 @@ public class AppInfoTest { Paths.get(APP_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME)); ele.removeAttribute(optField); AppInfo appInfo = new AppInfoFactory().createFromHrElement(ele, DEFAULT_VERSION); - appInfo.toOdDomElement(TestUtils.document()); + var unused = appInfo.toOdDomElement(TestUtils.document()); } for (String optField : OPTIONAL_FIELD_NAMES_OD) { @@ -211,7 +212,7 @@ public class AppInfoTest { Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME)); TestUtils.removeOdChildEleWithName(ele, optField); AppInfo appInfo = new AppInfoFactory().createFromOdElement(ele, DEFAULT_VERSION); - appInfo.toHrDomElement(TestUtils.document()); + var unused = appInfo.toHrDomElement(TestUtils.document()); } } diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java index b557fea9572b..cc58a61760f4 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java @@ -35,8 +35,6 @@ import javax.xml.parsers.ParserConfigurationException; @RunWith(JUnit4.class) public class DataLabelsTest { - private static final long DEFAULT_VERSION = 2L; - private static final String DATA_LABELS_HR_PATH = "com/android/asllib/datalabels/hr"; private static final String DATA_LABELS_OD_PATH = "com/android/asllib/datalabels/od"; diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java new file mode 100644 index 000000000000..2746800532ab --- /dev/null +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.asllib.marshallable; + +import static org.junit.Assert.assertThrows; + +import com.android.asllib.testutils.TestUtils; +import com.android.asllib.util.MalformedXmlException; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.w3c.dom.Element; + +import java.nio.file.Paths; +import java.util.List; + +@RunWith(JUnit4.class) +public class DeveloperInfoTest { + private static final String DEVELOPER_INFO_HR_PATH = "com/android/asllib/developerinfo/hr"; + private static final String DEVELOPER_INFO_OD_PATH = "com/android/asllib/developerinfo/od"; + public static final List<String> REQUIRED_FIELD_NAMES = + List.of("address", "countryRegion", "email", "name", "relationship"); + public static final List<String> REQUIRED_FIELD_NAMES_OD = + List.of("address", "country_region", "email", "name", "relationship"); + public static final List<String> OPTIONAL_FIELD_NAMES = List.of("website", "registryId"); + public static final List<String> OPTIONAL_FIELD_NAMES_OD = + List.of("website", "app_developer_registry_id"); + + private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml"; + + /** Logic for setting up tests (empty if not yet needed). */ + public static void main(String[] params) throws Exception {} + + @Before + public void setUp() throws Exception { + System.out.println("set up."); + } + + /** Test for all fields valid. */ + @Test + public void testAllFieldsValid() throws Exception { + System.out.println("starting testAllFieldsValid."); + testHrToOdDeveloperInfo(ALL_FIELDS_VALID_FILE_NAME); + testOdToHrDeveloperInfo(ALL_FIELDS_VALID_FILE_NAME); + } + + /** Tests missing required fields fails. */ + @Test + public void testMissingRequiredFields() throws Exception { + System.out.println("Starting testMissingRequiredFields"); + for (String reqField : REQUIRED_FIELD_NAMES) { + System.out.println("testing missing required field: " + reqField); + var developerInfoEle = + TestUtils.getElementFromResource( + Paths.get(DEVELOPER_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME)); + developerInfoEle.removeAttribute(reqField); + + assertThrows( + MalformedXmlException.class, + () -> new DeveloperInfoFactory().createFromHrElement(developerInfoEle)); + } + + for (String reqField : REQUIRED_FIELD_NAMES_OD) { + System.out.println("testing missing required field od: " + reqField); + var developerInfoEle = + TestUtils.getElementFromResource( + Paths.get(DEVELOPER_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME)); + TestUtils.removeOdChildEleWithName(developerInfoEle, reqField); + + assertThrows( + MalformedXmlException.class, + () -> new DeveloperInfoFactory().createFromOdElement(developerInfoEle)); + } + } + + /** Tests missing optional fields passes. */ + @Test + public void testMissingOptionalFields() throws Exception { + for (String optField : OPTIONAL_FIELD_NAMES) { + var developerInfoEle = + TestUtils.getElementFromResource( + Paths.get(DEVELOPER_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME)); + developerInfoEle.removeAttribute(optField); + DeveloperInfo developerInfo = + new DeveloperInfoFactory().createFromHrElement(developerInfoEle); + var unused = developerInfo.toOdDomElement(TestUtils.document()); + } + + for (String optField : OPTIONAL_FIELD_NAMES_OD) { + var developerInfoEle = + TestUtils.getElementFromResource( + Paths.get(DEVELOPER_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME)); + TestUtils.removeOdChildEleWithName(developerInfoEle, optField); + DeveloperInfo developerInfo = + new DeveloperInfoFactory().createFromOdElement(developerInfoEle); + var unused = developerInfo.toHrDomElement(TestUtils.document()); + } + } + + private void testHrToOdDeveloperInfo(String fileName) throws Exception { + var doc = TestUtils.document(); + DeveloperInfo developerInfo = + new DeveloperInfoFactory() + .createFromHrElement( + TestUtils.getElementFromResource( + Paths.get(DEVELOPER_INFO_HR_PATH, fileName))); + Element developerInfoEle = developerInfo.toOdDomElement(doc); + doc.appendChild(developerInfoEle); + TestUtils.testFormatToFormat(doc, Paths.get(DEVELOPER_INFO_OD_PATH, fileName)); + } + + private void testOdToHrDeveloperInfo(String fileName) throws Exception { + var doc = TestUtils.document(); + DeveloperInfo developerInfo = + new DeveloperInfoFactory() + .createFromOdElement( + TestUtils.getElementFromResource( + Paths.get(DEVELOPER_INFO_OD_PATH, fileName))); + Element developerInfoEle = developerInfo.toHrDomElement(doc); + doc.appendChild(developerInfoEle); + TestUtils.testFormatToFormat(doc, Paths.get(DEVELOPER_INFO_HR_PATH, fileName)); + } +} diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java index 7cd510f0ddfc..fc8ff00794ad 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java @@ -26,13 +26,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.w3c.dom.Element; -import org.xml.sax.SAXException; -import java.io.IOException; import java.nio.file.Paths; -import javax.xml.parsers.ParserConfigurationException; - @RunWith(JUnit4.class) public class SafetyLabelsTest { private static final long DEFAULT_VERSION = 2L; @@ -42,6 +38,8 @@ public class SafetyLabelsTest { private static final String VALID_EMPTY_FILE_NAME = "valid-empty.xml"; private static final String WITH_DATA_LABELS_FILE_NAME = "with-data-labels.xml"; + private static final String VALID_V1_FILE_NAME = "valid-v1.xml"; + private static final String UNRECOGNIZED_FIELD_V2_FILE_NAME = "unrecognized-field-v2.xml"; @Before public void setUp() throws Exception { @@ -52,61 +50,59 @@ public class SafetyLabelsTest { @Test public void testSafetyLabelsValidEmptyFile() throws Exception { System.out.println("starting testSafetyLabelsValidEmptyFile."); - testHrToOdSafetyLabels(VALID_EMPTY_FILE_NAME); - testOdToHrSafetyLabels(VALID_EMPTY_FILE_NAME); + testHrToOdSafetyLabels(VALID_EMPTY_FILE_NAME, DEFAULT_VERSION); + testOdToHrSafetyLabels(VALID_EMPTY_FILE_NAME, DEFAULT_VERSION); } /** Test for safety labels with data labels. */ @Test public void testSafetyLabelsWithDataLabels() throws Exception { System.out.println("starting testSafetyLabelsWithDataLabels."); - testHrToOdSafetyLabels(WITH_DATA_LABELS_FILE_NAME); - testOdToHrSafetyLabels(WITH_DATA_LABELS_FILE_NAME); + testHrToOdSafetyLabels(WITH_DATA_LABELS_FILE_NAME, DEFAULT_VERSION); + testOdToHrSafetyLabels(WITH_DATA_LABELS_FILE_NAME, DEFAULT_VERSION); } - private void hrToOdExpectException(String fileName) - throws ParserConfigurationException, IOException, SAXException { - var safetyLabelsEle = - TestUtils.getElementFromResource(Paths.get(SAFETY_LABELS_HR_PATH, fileName)); - assertThrows( - MalformedXmlException.class, - () -> - new SafetyLabelsFactory() - .createFromHrElement(safetyLabelsEle, DEFAULT_VERSION)); + /** Tests valid fields v1. */ + @Test + public void testValidFieldsV1() throws Exception { + var ele = + TestUtils.getElementFromResource( + Paths.get(SAFETY_LABELS_OD_PATH, VALID_V1_FILE_NAME)); + var unused = new SafetyLabelsFactory().createFromOdElement(ele, 1L); } - private void odToHrExpectException(String fileName) - throws ParserConfigurationException, IOException, SAXException { - var safetyLabelsEle = - TestUtils.getElementFromResource(Paths.get(SAFETY_LABELS_OD_PATH, fileName)); + /** Tests unrecognized field v2. */ + @Test + public void testUnrecognizedFieldV2() throws Exception { + var ele = + TestUtils.getElementFromResource( + Paths.get(SAFETY_LABELS_OD_PATH, VALID_V1_FILE_NAME)); assertThrows( MalformedXmlException.class, - () -> - new SafetyLabelsFactory() - .createFromOdElement(safetyLabelsEle, DEFAULT_VERSION)); + () -> new SafetyLabelsFactory().createFromOdElement(ele, 2L)); } - private void testHrToOdSafetyLabels(String fileName) throws Exception { + private void testHrToOdSafetyLabels(String fileName, long version) throws Exception { var doc = TestUtils.document(); SafetyLabels safetyLabels = new SafetyLabelsFactory() .createFromHrElement( TestUtils.getElementFromResource( Paths.get(SAFETY_LABELS_HR_PATH, fileName)), - DEFAULT_VERSION); + version); Element appInfoEle = safetyLabels.toOdDomElement(doc); doc.appendChild(appInfoEle); TestUtils.testFormatToFormat(doc, Paths.get(SAFETY_LABELS_OD_PATH, fileName)); } - private void testOdToHrSafetyLabels(String fileName) throws Exception { + private void testOdToHrSafetyLabels(String fileName, long version) throws Exception { var doc = TestUtils.document(); SafetyLabels safetyLabels = new SafetyLabelsFactory() .createFromOdElement( TestUtils.getElementFromResource( Paths.get(SAFETY_LABELS_OD_PATH, fileName)), - DEFAULT_VERSION); + version); Element appInfoEle = safetyLabels.toHrDomElement(doc); doc.appendChild(appInfoEle); TestUtils.testFormatToFormat(doc, Paths.get(SAFETY_LABELS_HR_PATH, fileName)); diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java new file mode 100644 index 000000000000..27f8720868dc --- /dev/null +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.asllib.marshallable; + +import com.android.asllib.testutils.TestUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.w3c.dom.Element; + +import java.nio.file.Paths; +import java.util.List; + +@RunWith(JUnit4.class) +public class SecurityLabelsTest { + private static final String SECURITY_LABELS_HR_PATH = "com/android/asllib/securitylabels/hr"; + private static final String SECURITY_LABELS_OD_PATH = "com/android/asllib/securitylabels/od"; + + public static final List<String> OPTIONAL_FIELD_NAMES = + List.of("isDataDeletable", "isDataEncrypted"); + public static final List<String> OPTIONAL_FIELD_NAMES_OD = + List.of("is_data_deletable", "is_data_encrypted"); + + private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml"; + + /** Logic for setting up tests (empty if not yet needed). */ + public static void main(String[] params) throws Exception {} + + @Before + public void setUp() throws Exception { + System.out.println("set up."); + } + + /** Test for all fields valid. */ + @Test + public void testAllFieldsValid() throws Exception { + System.out.println("starting testAllFieldsValid."); + testHrToOdSecurityLabels(ALL_FIELDS_VALID_FILE_NAME); + testOdToHrSecurityLabels(ALL_FIELDS_VALID_FILE_NAME); + } + + /** Tests missing optional fields passes. */ + @Test + public void testMissingOptionalFields() throws Exception { + for (String optField : OPTIONAL_FIELD_NAMES) { + var ele = + TestUtils.getElementFromResource( + Paths.get(SECURITY_LABELS_HR_PATH, ALL_FIELDS_VALID_FILE_NAME)); + ele.removeAttribute(optField); + SecurityLabels securityLabels = new SecurityLabelsFactory().createFromHrElement(ele); + var unused = securityLabels.toOdDomElement(TestUtils.document()); + } + for (String optField : OPTIONAL_FIELD_NAMES_OD) { + var ele = + TestUtils.getElementFromResource( + Paths.get(SECURITY_LABELS_OD_PATH, ALL_FIELDS_VALID_FILE_NAME)); + TestUtils.removeOdChildEleWithName(ele, optField); + SecurityLabels securityLabels = new SecurityLabelsFactory().createFromOdElement(ele); + var unused = securityLabels.toHrDomElement(TestUtils.document()); + } + } + + private void testHrToOdSecurityLabels(String fileName) throws Exception { + var doc = TestUtils.document(); + SecurityLabels securityLabels = + new SecurityLabelsFactory() + .createFromHrElement( + TestUtils.getElementFromResource( + Paths.get(SECURITY_LABELS_HR_PATH, fileName))); + Element ele = securityLabels.toOdDomElement(doc); + doc.appendChild(ele); + TestUtils.testFormatToFormat(doc, Paths.get(SECURITY_LABELS_OD_PATH, fileName)); + } + + private void testOdToHrSecurityLabels(String fileName) throws Exception { + var doc = TestUtils.document(); + SecurityLabels securityLabels = + new SecurityLabelsFactory() + .createFromOdElement( + TestUtils.getElementFromResource( + Paths.get(SECURITY_LABELS_OD_PATH, fileName))); + Element ele = securityLabels.toHrDomElement(doc); + doc.appendChild(ele); + TestUtils.testFormatToFormat(doc, Paths.get(SECURITY_LABELS_HR_PATH, fileName)); + } +} diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java index 9dcc6529969e..04bcd783a1dd 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java @@ -43,6 +43,7 @@ public class SystemAppSafetyLabelTest { "com/android/asllib/systemappsafetylabel/od"; private static final String VALID_FILE_NAME = "valid.xml"; + private static final String VALID_V1_FILE_NAME = "valid-v1.xml"; private static final String MISSING_BOOL_FILE_NAME = "missing-bool.xml"; /** Logic for setting up tests (empty if not yet needed). */ @@ -57,59 +58,81 @@ public class SystemAppSafetyLabelTest { @Test public void testValid() throws Exception { System.out.println("starting testValid."); - testHrToOdSystemAppSafetyLabel(VALID_FILE_NAME); - testOdToHrSystemAppSafetyLabel(VALID_FILE_NAME); + testHrToOdSystemAppSafetyLabel(VALID_FILE_NAME, DEFAULT_VERSION); + testOdToHrSystemAppSafetyLabel(VALID_FILE_NAME, DEFAULT_VERSION); + } + + /** Test for valid v1. */ + @Test + public void testValidV1() throws Exception { + System.out.println("starting testValidV1."); + var doc = TestUtils.document(); + var unused = + new SystemAppSafetyLabelFactory() + .createFromOdElement( + TestUtils.getElementFromResource( + Paths.get( + SYSTEM_APP_SAFETY_LABEL_OD_PATH, + VALID_V1_FILE_NAME)), + 1L); + } + + /** Test for testV1InvalidAsV2. */ + @Test + public void testV1InvalidAsV2() throws Exception { + System.out.println("starting testV1InvalidAsV2."); + odToHrExpectException(VALID_V1_FILE_NAME, 2L); } /** Tests missing bool. */ @Test public void testMissingBool() throws Exception { System.out.println("starting testMissingBool."); - hrToOdExpectException(MISSING_BOOL_FILE_NAME); - odToHrExpectException(MISSING_BOOL_FILE_NAME); + hrToOdExpectException(MISSING_BOOL_FILE_NAME, DEFAULT_VERSION); + odToHrExpectException(MISSING_BOOL_FILE_NAME, DEFAULT_VERSION); } - private void hrToOdExpectException(String fileName) + private void hrToOdExpectException(String fileName, long version) throws ParserConfigurationException, IOException, SAXException { var ele = TestUtils.getElementFromResource( Paths.get(SYSTEM_APP_SAFETY_LABEL_HR_PATH, fileName)); assertThrows( MalformedXmlException.class, - () -> new SystemAppSafetyLabelFactory().createFromHrElement(ele, DEFAULT_VERSION)); + () -> new SystemAppSafetyLabelFactory().createFromHrElement(ele, version)); } - private void odToHrExpectException(String fileName) + private void odToHrExpectException(String fileName, long version) throws ParserConfigurationException, IOException, SAXException { var ele = TestUtils.getElementFromResource( Paths.get(SYSTEM_APP_SAFETY_LABEL_OD_PATH, fileName)); assertThrows( MalformedXmlException.class, - () -> new SystemAppSafetyLabelFactory().createFromOdElement(ele, DEFAULT_VERSION)); + () -> new SystemAppSafetyLabelFactory().createFromOdElement(ele, version)); } - private void testHrToOdSystemAppSafetyLabel(String fileName) throws Exception { + private void testHrToOdSystemAppSafetyLabel(String fileName, long version) throws Exception { var doc = TestUtils.document(); SystemAppSafetyLabel systemAppSafetyLabel = new SystemAppSafetyLabelFactory() .createFromHrElement( TestUtils.getElementFromResource( Paths.get(SYSTEM_APP_SAFETY_LABEL_HR_PATH, fileName)), - DEFAULT_VERSION); + version); Element resultingEle = systemAppSafetyLabel.toOdDomElement(doc); doc.appendChild(resultingEle); TestUtils.testFormatToFormat(doc, Paths.get(SYSTEM_APP_SAFETY_LABEL_OD_PATH, fileName)); } - private void testOdToHrSystemAppSafetyLabel(String fileName) throws Exception { + private void testOdToHrSystemAppSafetyLabel(String fileName, long version) throws Exception { var doc = TestUtils.document(); SystemAppSafetyLabel systemAppSafetyLabel = new SystemAppSafetyLabelFactory() .createFromOdElement( TestUtils.getElementFromResource( Paths.get(SYSTEM_APP_SAFETY_LABEL_OD_PATH, fileName)), - DEFAULT_VERSION); + version); Element resultingEle = systemAppSafetyLabel.toHrDomElement(doc); doc.appendChild(resultingEle); TestUtils.testFormatToFormat(doc, Paths.get(SYSTEM_APP_SAFETY_LABEL_HR_PATH, fileName)); diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java new file mode 100644 index 000000000000..ebb2e93af920 --- /dev/null +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.asllib.marshallable; + +import static org.junit.Assert.assertThrows; + +import com.android.asllib.testutils.TestUtils; +import com.android.asllib.util.MalformedXmlException; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.w3c.dom.Element; + +import java.nio.file.Paths; + +@RunWith(JUnit4.class) +public class ThirdPartyVerificationTest { + private static final String THIRD_PARTY_VERIFICATION_HR_PATH = + "com/android/asllib/thirdpartyverification/hr"; + private static final String THIRD_PARTY_VERIFICATION_OD_PATH = + "com/android/asllib/thirdpartyverification/od"; + + private static final String VALID_FILE_NAME = "valid.xml"; + private static final String MISSING_URL_FILE_NAME = "missing-url.xml"; + + /** Logic for setting up tests (empty if not yet needed). */ + public static void main(String[] params) throws Exception {} + + @Before + public void setUp() throws Exception { + System.out.println("set up."); + } + + /** Test for valid. */ + @Test + public void testValid() throws Exception { + System.out.println("starting testValid."); + testHrToOdThirdPartyVerification(VALID_FILE_NAME); + testOdToHrThirdPartyVerification(VALID_FILE_NAME); + } + + /** Tests missing url. */ + @Test + public void testMissingUrl() throws Exception { + System.out.println("starting testMissingUrl."); + hrToOdExpectException(MISSING_URL_FILE_NAME); + odToHrExpectException(MISSING_URL_FILE_NAME); + } + + private void hrToOdExpectException(String fileName) { + assertThrows( + MalformedXmlException.class, + () -> { + new ThirdPartyVerificationFactory() + .createFromHrElement( + TestUtils.getElementFromResource( + Paths.get(THIRD_PARTY_VERIFICATION_HR_PATH, fileName))); + }); + } + + private void odToHrExpectException(String fileName) { + assertThrows( + MalformedXmlException.class, + () -> { + new ThirdPartyVerificationFactory() + .createFromOdElement( + TestUtils.getElementFromResource( + Paths.get(THIRD_PARTY_VERIFICATION_OD_PATH, fileName))); + }); + } + + private void testHrToOdThirdPartyVerification(String fileName) throws Exception { + var doc = TestUtils.document(); + ThirdPartyVerification thirdPartyVerification = + new ThirdPartyVerificationFactory() + .createFromHrElement( + TestUtils.getElementFromResource( + Paths.get(THIRD_PARTY_VERIFICATION_HR_PATH, fileName))); + Element ele = thirdPartyVerification.toOdDomElement(doc); + doc.appendChild(ele); + TestUtils.testFormatToFormat(doc, Paths.get(THIRD_PARTY_VERIFICATION_OD_PATH, fileName)); + } + + private void testOdToHrThirdPartyVerification(String fileName) throws Exception { + var doc = TestUtils.document(); + ThirdPartyVerification thirdPartyVerification = + new ThirdPartyVerificationFactory() + .createFromOdElement( + TestUtils.getElementFromResource( + Paths.get(THIRD_PARTY_VERIFICATION_OD_PATH, fileName))); + Element ele = thirdPartyVerification.toHrDomElement(doc); + doc.appendChild(ele); + TestUtils.testFormatToFormat(doc, Paths.get(THIRD_PARTY_VERIFICATION_HR_PATH, fileName)); + } +} diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java index 6547fb952944..b27d6ddb6243 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java @@ -16,16 +16,23 @@ package com.android.asllib.marshallable; +import static org.junit.Assert.assertThrows; + import com.android.asllib.testutils.TestUtils; +import com.android.asllib.util.MalformedXmlException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.w3c.dom.Element; +import org.xml.sax.SAXException; +import java.io.IOException; import java.nio.file.Paths; +import javax.xml.parsers.ParserConfigurationException; + @RunWith(JUnit4.class) public class TransparencyInfoTest { private static final long DEFAULT_VERSION = 2L; @@ -35,6 +42,10 @@ public class TransparencyInfoTest { private static final String TRANSPARENCY_INFO_OD_PATH = "com/android/asllib/transparencyinfo/od"; private static final String WITH_APP_INFO_FILE_NAME = "with-app-info.xml"; + private static final String VALID_EMPTY_V1_FILE_NAME = "valid-empty-v1.xml"; + private static final String VALID_DEV_INFO_V1_FILE_NAME = "valid-dev-info-v1.xml"; + private static final String WITH_APP_INFO_AND_DEV_INFO_FILE_NAME = + "with-app-info-v2-and-dev-info-v1.xml"; @Before public void setUp() throws Exception { @@ -45,33 +56,78 @@ public class TransparencyInfoTest { @Test public void testTransparencyInfoWithAppInfo() throws Exception { System.out.println("starting testTransparencyInfoWithAppInfo."); - testHrToOdTransparencyInfo(WITH_APP_INFO_FILE_NAME); - testOdToHrTransparencyInfo(WITH_APP_INFO_FILE_NAME); + testHrToOdTransparencyInfo(WITH_APP_INFO_FILE_NAME, DEFAULT_VERSION); + testOdToHrTransparencyInfo(WITH_APP_INFO_FILE_NAME, DEFAULT_VERSION); + } + + /** Test for testMissingAppInfoFailsInV2. */ + @Test + public void testMissingAppInfoFailsInV2() throws Exception { + System.out.println("starting testMissingAppInfoFailsInV2."); + odToHrExpectException(VALID_EMPTY_V1_FILE_NAME, 2L); + } + + /** Test for testMissingAppInfoPassesInV1. */ + @Test + public void testMissingAppInfoPassesInV1() throws Exception { + System.out.println("starting testMissingAppInfoPassesInV1."); + testParseOdTransparencyInfo(VALID_EMPTY_V1_FILE_NAME, 1L); + } + + /** Test for testDeveloperInfoExistencePassesInV1. */ + @Test + public void testDeveloperInfoExistencePassesInV1() throws Exception { + System.out.println("starting testDeveloperInfoExistencePassesInV1."); + testParseOdTransparencyInfo(VALID_DEV_INFO_V1_FILE_NAME, 1L); } - private void testHrToOdTransparencyInfo(String fileName) throws Exception { + /** Test for testDeveloperInfoExistenceFailsInV2. */ + @Test + public void testDeveloperInfoExistenceFailsInV2() throws Exception { + System.out.println("starting testDeveloperInfoExistenceFailsInV2."); + odToHrExpectException(WITH_APP_INFO_AND_DEV_INFO_FILE_NAME, 2L); + } + + private void testHrToOdTransparencyInfo(String fileName, long version) throws Exception { var doc = TestUtils.document(); TransparencyInfo transparencyInfo = new TransparencyInfoFactory() .createFromHrElement( TestUtils.getElementFromResource( Paths.get(TRANSPARENCY_INFO_HR_PATH, fileName)), - DEFAULT_VERSION); + version); Element resultingEle = transparencyInfo.toOdDomElement(doc); doc.appendChild(resultingEle); TestUtils.testFormatToFormat(doc, Paths.get(TRANSPARENCY_INFO_OD_PATH, fileName)); } - private void testOdToHrTransparencyInfo(String fileName) throws Exception { + private void testParseOdTransparencyInfo(String fileName, long version) throws Exception { + var unused = + new TransparencyInfoFactory() + .createFromOdElement( + TestUtils.getElementFromResource( + Paths.get(TRANSPARENCY_INFO_OD_PATH, fileName)), + version); + } + + private void testOdToHrTransparencyInfo(String fileName, long version) throws Exception { var doc = TestUtils.document(); TransparencyInfo transparencyInfo = new TransparencyInfoFactory() .createFromOdElement( TestUtils.getElementFromResource( Paths.get(TRANSPARENCY_INFO_OD_PATH, fileName)), - DEFAULT_VERSION); + version); Element resultingEle = transparencyInfo.toHrDomElement(doc); doc.appendChild(resultingEle); TestUtils.testFormatToFormat(doc, Paths.get(TRANSPARENCY_INFO_HR_PATH, fileName)); } + + private void odToHrExpectException(String fileName, long version) + throws ParserConfigurationException, IOException, SAXException { + var ele = TestUtils.getElementFromResource(Paths.get(TRANSPARENCY_INFO_OD_PATH, fileName)); + assertThrows( + MalformedXmlException.class, + () -> new TransparencyInfoFactory().createFromOdElement(ele, version)); + } } diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/valid-v1.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/valid-v1.xml new file mode 100644 index 000000000000..7e984e333ceb --- /dev/null +++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/valid-v1.xml @@ -0,0 +1,8 @@ +<bundle> + <long name="version" value="1"/> + <pbundle_as_map name="system_app_safety_label"> + <string name="url" value="www.example.com"/> + </pbundle_as_map> + <pbundle_as_map name="transparency_info"> + </pbundle_as_map> +</bundle>
\ No newline at end of file diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/unrecognized-v1.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/unrecognized-v1.xml index 810078e777fb..01fd7180c3a6 100644 --- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/unrecognized-v1.xml +++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/unrecognized-v1.xml @@ -21,5 +21,5 @@ <string name="category" value="Food and drink"/> <string name="email" value="max@maxloh.com"/> <string name="website" value="www.example.com"/> - <string name="unrecognized" value="www.example.com"/> + <boolean name="aps_compliant" value="false"/> </pbundle_as_map> diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-v1.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-v1.xml new file mode 100644 index 000000000000..1384a2f6dd52 --- /dev/null +++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-v1.xml @@ -0,0 +1,9 @@ +<pbundle_as_map name="safety_labels"> + <pbundle_as_map name="security_labels"> + <boolean name="is_data_deletable" value="true" /> + <boolean name="is_data_encrypted" value="false" /> + </pbundle_as_map> + <pbundle_as_map name="third_party_verification"> + <string name="url" value="www.example.com"/> + </pbundle_as_map> +</pbundle_as_map>
\ No newline at end of file diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid-v1.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid-v1.xml new file mode 100644 index 000000000000..f96535b4b49b --- /dev/null +++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid-v1.xml @@ -0,0 +1,3 @@ +<pbundle_as_map name="system_app_safety_label"> + <string name="url" value="www.example.com"/> +</pbundle_as_map>
\ No newline at end of file diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-dev-info-v1.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-dev-info-v1.xml new file mode 100644 index 000000000000..d7a4e1a959b7 --- /dev/null +++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-dev-info-v1.xml @@ -0,0 +1,12 @@ + +<pbundle_as_map name="transparency_info"> + <pbundle_as_map name="developer_info"> + <string name="name" value="max"/> + <string name="email" value="max@example.com"/> + <string name="address" value="111 blah lane"/> + <string name="country_region" value="US"/> + <long name="relationship" value="5"/> + <string name="website" value="example.com"/> + <string name="app_developer_registry_id" value="registry_id"/> + </pbundle_as_map> +</pbundle_as_map>
\ No newline at end of file diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-empty.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-empty-v1.xml index af574cf92b3a..af574cf92b3a 100644 --- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-empty.xml +++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/valid-empty-v1.xml diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info-v2-and-dev-info-v1.xml index b5e64b925ca5..b5e64b925ca5 100644 --- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-developer-info.xml +++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info-v2-and-dev-info-v1.xml diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt new file mode 100644 index 000000000000..c67e6714d4c2 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 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.hoststubgen.filters + +import com.android.hoststubgen.asm.toHumanReadableClassName +import com.android.hoststubgen.utils.Trie + +/** + * Filter to apply a policy to classes inside a package, either directly or indirectly. + */ +class PackageFilter( + fallback: OutputFilter +) : DelegatingFilter(fallback) { + + private val mPackagePolicies = PackagePolicyTrie() + + // We want to pick the most specific filter for a package name. + // Since any package with a matching prefix is a valid match, we can use a prefix tree + // to help us find the nearest matching filter. + private class PackagePolicyTrie : Trie<String, String, FilterPolicyWithReason>() { + // Split package name into individual component + override fun splitToComponents(key: String): Iterator<String> { + return key.split('.').iterator() + } + } + + private fun getPackageKey(packageName: String): String { + return packageName.toHumanReadableClassName() + } + + private fun getPackageKeyFromClass(className: String): String { + val clazz = className.toHumanReadableClassName() + val idx = clazz.lastIndexOf('.') + return if (idx >= 0) clazz.substring(0, idx) else "" + } + + /** + * Add a policy to all classes inside a package, either directly or indirectly. + */ + fun addPolicy(packageName: String, policy: FilterPolicyWithReason) { + mPackagePolicies[getPackageKey(packageName)] = policy + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + return mPackagePolicies[getPackageKeyFromClass(className)] + ?: super.getPolicyForClass(className) + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt index c5acd81f1cf2..a89824eaf0b0 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt @@ -64,7 +64,8 @@ fun createFilterFromTextPolicyFile( log.i("Loading offloaded annotations from $filename ...") log.withIndent { val subclassFilter = SubclassFilter(classes, fallback) - val imf = InMemoryOutputFilter(classes, subclassFilter) + val packageFilter = PackageFilter(subclassFilter) + val imf = InMemoryOutputFilter(classes, packageFilter) var lineNo = 0 @@ -78,10 +79,7 @@ fun createFilterFromTextPolicyFile( var className = "" while (true) { - var line = reader.readLine() - if (line == null) { - break - } + var line = reader.readLine() ?: break lineNo++ line = normalizeTextLine(line) @@ -95,6 +93,31 @@ fun createFilterFromTextPolicyFile( val fields = line.split(whitespaceRegex).toTypedArray() when (fields[0].lowercase()) { + "p", "package" -> { + if (fields.size < 3) { + throw ParseException("Package ('p') expects 2 fields.") + } + val name = fields[1] + val rawPolicy = fields[2] + if (resolveExtendingClass(name) != null) { + throw ParseException("Package can't be a super class type") + } + if (resolveSpecialClass(name) != SpecialClass.NotSpecial) { + throw ParseException("Package can't be a special class type") + } + if (rawPolicy.startsWith("!")) { + throw ParseException("Package can't have a substitution") + } + if (rawPolicy.startsWith("~")) { + throw ParseException("Package can't have a class load hook") + } + val policy = parsePolicy(rawPolicy) + if (!policy.isUsableWithClasses) { + throw ParseException("Package can't have policy '$policy'") + } + packageFilter.addPolicy(name, policy.withReason(FILTER_REASON)) + } + "c", "class" -> { if (fields.size < 3) { throw ParseException("Class ('c') expects 2 fields.") diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt new file mode 100644 index 000000000000..1b3d79cddb8e --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 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.hoststubgen.utils + +abstract class Trie<Key, Component, Value> { + + private val root = TrieNode<Component, Value>() + + abstract fun splitToComponents(key: Key): Iterator<Component> + + operator fun set(key: Key, value: Value) { + val node = root.getExactNode(splitToComponents(key)) + node.value = value + } + + operator fun get(key: Key): Value? { + return root.getNearestValue(null, splitToComponents(key)) + } + + private class TrieNode<Component, Value> { + private val children = mutableMapOf<Component, TrieNode<Component, Value>>() + var value: Value? = null + + fun getExactNode(components: Iterator<Component>): TrieNode<Component, Value> { + val n = components.next() + val child = children.getOrPut(n) { TrieNode() } + return if (components.hasNext()) { + child.getExactNode(components) + } else { + child + } + } + + fun getNearestValue(current: Value?, components: Iterator<Component>): Value? { + val n = components.next() + val child = children[n] ?: return current + val newValue = child.value ?: current + return if (components.hasNext()) { + child.getNearestValue(newValue, components) + } else { + newValue + } + } + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt index dd638925a5bc..3ef117567482 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt @@ -2706,6 +2706,98 @@ SourceFile: "TinyFrameworkPackageRedirect.java" RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.packagetest.A(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/packagetest/A; +} +SourceFile: "A.java" +## Class: com/android/hoststubgen/test/tinyframework/packagetest/B.class + Compiled from "B.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.B + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/B + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.packagetest.B(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/packagetest/B; +} +SourceFile: "B.java" +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.packagetest.sub.A(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/packagetest/sub/A; +} +SourceFile: "A.java" +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/B.class + Compiled from "B.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.B + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/B + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.packagetest.sub.B(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/packagetest/sub/B; +} +SourceFile: "B.java" ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class Compiled from "C1.java" public class com.android.hoststubgen.test.tinyframework.subclasstest.C1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt index 906a81cf45e3..0bbb4182859b 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt @@ -2177,6 +2177,38 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class Compiled from "C1.java" public class com.android.hoststubgen.test.tinyframework.subclasstest.C1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt index 10bc91da2544..57f3783e8a73 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt @@ -3540,6 +3540,38 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class Compiled from "C1.java" public class com.android.hoststubgen.test.tinyframework.subclasstest.C1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt index 906a81cf45e3..0bbb4182859b 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt @@ -2177,6 +2177,38 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class Compiled from "C1.java" public class com.android.hoststubgen.test.tinyframework.subclasstest.C1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt index fcf9a8c663ad..91104dea3f7b 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt @@ -4408,6 +4408,56 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/packagetest/A + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/packagetest/sub/A + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class Compiled from "C1.java" public class com.android.hoststubgen.test.tinyframework.subclasstest.C1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt index 696b6d009dc2..530de431828e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt @@ -40,3 +40,11 @@ class *com.android.hoststubgen.test.tinyframework.subclasstest.CA remove class *com.android.hoststubgen.test.tinyframework.subclasstest.I1 keep class *com.android.hoststubgen.test.tinyframework.subclasstest.IA remove + +# Test package directive +package com.android.hoststubgen.test.tinyframework.packagetest stub +class com.android.hoststubgen.test.tinyframework.packagetest.B remove +class com.android.hoststubgen.test.tinyframework.packagetest.sub.B remove +# The following rules are the same as above +# class com.android.hoststubgen.test.tinyframework.packagetest.A stub +# class com.android.hoststubgen.test.tinyframework.packagetest.sub.A stub diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java new file mode 100644 index 000000000000..6a52e4401b45 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 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.hoststubgen.test.tinyframework.packagetest; + +public class A { +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java new file mode 100644 index 000000000000..1374a288f7aa --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 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.hoststubgen.test.tinyframework.packagetest; + +public class B { +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java new file mode 100644 index 000000000000..361a7fd04842 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 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.hoststubgen.test.tinyframework.packagetest.sub; + +public class A { +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java new file mode 100644 index 000000000000..716595a44243 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 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.hoststubgen.test.tinyframework.packagetest.sub; + +public class B { +} diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt new file mode 100644 index 000000000000..081d03909926 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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.hoststubgen.utils + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class TrieTest { + + private class TestTrie : Trie<String, Char, Int>() { + override fun splitToComponents(key: String): Iterator<Char> { + return key.toCharArray().iterator() + } + } + + @Test + fun testPrefixTree() { + val trie = TestTrie() + trie["ab"] = 1 + trie["abc"] = 2 + trie["ab123"] = 3 + assertNull(trie["a"]) + assertNull(trie["x"]) + assertNull(trie["a1"]) + assertEquals(1, trie["ab"]) + assertEquals(2, trie["abc"]) + assertEquals(2, trie["abcd"]) + assertEquals(1, trie["ab1"]) + assertEquals(1, trie["ab12"]) + assertEquals(3, trie["ab123"]) + assertEquals(1, trie["ab@"]) + } +} diff --git a/tools/lint/fix/README.md b/tools/lint/fix/README.md index a5ac2be1c18a..18bda9287a50 100644 --- a/tools/lint/fix/README.md +++ b/tools/lint/fix/README.md @@ -6,7 +6,7 @@ Inspiration: go/refactor-the-platform-with-lint\ It's a python script that runs the framework linter, and then (optionally) copies modified files back into the source tree.\ -Why python, you ask? Because python is cool ¯\_(ツ)_/¯. +Why python, you ask? Because python is cool ¯\\\_(ツ)\_/¯. Incidentally, this exposes a much simpler way to run individual lint checks against individual modules, so it's useful beyond applying fixes. @@ -15,7 +15,7 @@ against individual modules, so it's useful beyond applying fixes. Lint is not allowed to modify source files directly via lint's `--apply-suggestions` flag. As a compromise, soong zips up the (potentially) modified sources and leaves them in an intermediate -directory. This script runs the lint, unpacks those files, and copies them back into the tree. +directory. This script runs the lint, unpacks those files, and copies them back into the tree. ## How do I run it? **WARNING: You probably want to commit/stash any changes to your working tree before doing this...** |