diff options
author | Giulio Fiscella <fiscella@google.com> | 2023-04-23 20:10:09 +0000 |
---|---|---|
committer | Giulio Fiscella <fiscella@google.com> | 2023-04-23 20:34:16 +0000 |
commit | 47364de5c70175ca7b569cb354e79bc3aa82905e (patch) | |
tree | 043610301faf965ba817969d324319d51a71b1ce /SafetyCenter | |
parent | 7e0363366c1ea30301cf7d298083d976af097419 (diff) | |
download | Permission-47364de5c70175ca7b569cb354e79bc3aa82905e.tar.gz |
Only use EN-US strings to lint config
When we delete strings, we tend to delete only the EN-US version and we
then rely on the translation pipeline to delete all translations. The
linter would have reported a false positive if a string is deleted
without deleting the reference in the config, because it would fallback
on a translation. (Note that the actual code on the device would not
fallback if the device is set to EN-US).
Also, improve the string checks by taking into consideration which SDK a
string is available from.
Bug: 257974388
Test: m out/soong/.intermediates/packages/modules/Permission/SafetyCenter/Resources/SafetyCenterResources/android_common/lint/lint-report.html
Test: atest ConfigLintCheckerTest
Change-Id: I7b8bfa432c77f06fd24b16d271d7c8a4a008d021
Diffstat (limited to 'SafetyCenter')
3 files changed, 85 insertions, 21 deletions
diff --git a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt index f93d4ecab..2d095967b 100644 --- a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt +++ b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt @@ -50,11 +50,26 @@ object FileSdk { if (lastQualifier.isEmpty() || lastQualifier[0] != 'v') { return TIRAMISU } - return try { - lastQualifier.substring(1).toInt() - } catch (nfe: NumberFormatException) { - TIRAMISU + return lastQualifier.substring(1).toIntOrNull() ?: TIRAMISU + } + + /** + * Returns whether the file belongs to a basic configuration. By basic, we mean either the + * default configuration that has no qualifier, or a configuration that is defined only by an + * SDK level version. + */ + fun belongsToABasicConfiguration(file: File): Boolean { + val directParentName = file.parentFile.name + val qualifierCount = directParentName.count { it == '-' } + val lastQualifier = directParentName.substringAfterLast("-", "") + if ( + lastQualifier.isNotEmpty() && + lastQualifier[0] == 'v' && + lastQualifier.substring(1).toIntOrNull() != null + ) { + return qualifierCount == 1 } + return qualifierCount == 0 } /** Returns the schema for the specific SDK level provided or null if it doesn't exist. */ diff --git a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt index d8656f889..c05b5404c 100644 --- a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt +++ b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt @@ -35,6 +35,7 @@ import com.android.tools.lint.detector.api.Severity import com.android.tools.lint.detector.api.XmlContext import com.android.tools.lint.detector.api.XmlScanner import java.util.EnumSet +import kotlin.math.min import org.w3c.dom.Element import org.w3c.dom.Node @@ -60,8 +61,8 @@ class ParserExceptionDetector : Detector(), OtherFileScanner, XmlScanner { androidSpecific = true ) - val STRING_MAP_BUILD_PHASE = 1 - val CONFIG_PARSE_PHASE = 2 + const val STRING_MAP_BUILD_PHASE = 1 + const val CONFIG_PARSE_PHASE = 2 } override fun appliesTo(folderType: ResourceFolderType): Boolean { @@ -73,9 +74,10 @@ class ParserExceptionDetector : Detector(), OtherFileScanner, XmlScanner { } /** Implements XmlScanner and builds a map of string resources in the first phase */ - val mNameToIndex: MutableMap<String, Int> = mutableMapOf() - val mIndexToValue: MutableMap<Int, String> = mutableMapOf() - var mIndex = 1000 + private val mNameToIndex: MutableMap<String, Int> = mutableMapOf() + private val mIndexToValue: MutableMap<Int, String> = mutableMapOf() + private val mIndexToMinSdk: MutableMap<Int, Int> = mutableMapOf() + private var mIndex = 1000 override fun getApplicableElements(): Collection<String>? { return listOf(TAG_STRING) @@ -84,14 +86,21 @@ class ParserExceptionDetector : Detector(), OtherFileScanner, XmlScanner { override fun visitElement(context: XmlContext, element: Element) { if ( context.driver.phase != STRING_MAP_BUILD_PHASE || - context.resourceFolderType != ResourceFolderType.VALUES + context.resourceFolderType != ResourceFolderType.VALUES || + !FileSdk.belongsToABasicConfiguration(context.file) ) { return } + val minSdk = FileSdk.getSdkQualifier(context.file) val name = element.getAttribute(ATTR_NAME) + val index = mNameToIndex[name] + if (index != null) { + mIndexToMinSdk[index] = min(mIndexToMinSdk[index]!!, minSdk) + return + } var value = "" - for (index in 0 until element.childNodes.length) { - val child = element.childNodes.item(index) + for (childIndex in 0 until element.childNodes.length) { + val child = element.childNodes.item(childIndex) if (child.nodeType == Node.TEXT_NODE) { value = child.nodeValue break @@ -99,6 +108,7 @@ class ParserExceptionDetector : Detector(), OtherFileScanner, XmlScanner { } mNameToIndex[name] = mIndex mIndexToValue[mIndex] = value + mIndexToMinSdk[mIndex] = minSdk mIndex++ } @@ -129,7 +139,11 @@ class ParserExceptionDetector : Detector(), OtherFileScanner, XmlScanner { // the target package or on packages that refer to Android global resources. // However, we cannot use a custom linter with the default soong overlay // build rule regardless. - Resources(context.project.`package`, mNameToIndex, mIndexToValue) + Resources( + context.project.`package`, + mNameToIndex.filterValues { sdk >= mIndexToMinSdk[it]!! }, + mIndexToValue.filterKeys { sdk >= mIndexToMinSdk[it]!! } + ) ) } catch (e: ParseException) { context.report( diff --git a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt index 869780eea..1cd693ef6 100644 --- a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt +++ b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt @@ -16,6 +16,7 @@ package android.safetycenter.lint.test +import android.os.Build.VERSION_CODES.TIRAMISU import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.lint.FileSdk import android.safetycenter.lint.ParserExceptionDetector @@ -48,7 +49,7 @@ class ParserExceptionDetectorTest : LintDetectorTest() { lint() .files( xml("res/raw/safety_center_config.xml", VALID_TIRAMISU_CONFIG), - STRINGS_WITH_REFERENCE_XML + STRINGS_WITH_REFERENCE_XML_DEFAULT ) .run() .expectClean() @@ -59,7 +60,7 @@ class ParserExceptionDetectorTest : LintDetectorTest() { lint() .files( xml("res/raw-99/safety_center_config.xml", VALID_TIRAMISU_CONFIG), - STRINGS_WITH_REFERENCE_XML + STRINGS_WITH_REFERENCE_XML_DEFAULT ) .run() .expectClean() @@ -100,7 +101,7 @@ class ParserExceptionDetectorTest : LintDetectorTest() { lint() .files( xml("res/raw/safety_center_config.xml", VALID_UDC_CONFIG), - STRINGS_WITH_REFERENCE_XML + STRINGS_WITH_REFERENCE_XML_DEFAULT ) .run() .expect( @@ -111,6 +112,40 @@ class ParserExceptionDetectorTest : LintDetectorTest() { } @Test + fun validTConfigWithUReference_throwsInT() { + FileSdk.maxVersionOverride = TIRAMISU + lint() + .files( + xml("res/raw/safety_center_config.xml", VALID_TIRAMISU_CONFIG), + xml("res/values-v34/strings.xml", STRINGS_WITH_REFERENCE_XML), + ) + .run() + .expect( + "res/raw/safety_center_config.xml: Error: Parser exception at sdk=33: " + + "\"Resource name \"@lint.test.pkg:string/reference\" in " + + "safety-sources-group.title missing or invalid\", cause: \"null\" " + + "[InvalidSafetyCenterConfig]\n1 errors, 0 warnings" + ) + } + + @Test + fun validTConfigWithOtherLanguageReference_throwsInT() { + FileSdk.maxVersionOverride = TIRAMISU + lint() + .files( + xml("res/raw/safety_center_config.xml", VALID_TIRAMISU_CONFIG), + xml("res/values-ar/strings.xml", STRINGS_WITH_REFERENCE_XML), + ) + .run() + .expect( + "res/raw/safety_center_config.xml: Error: Parser exception at sdk=33: " + + "\"Resource name \"@lint.test.pkg:string/reference\" in " + + "safety-sources-group.title missing or invalid\", cause: \"null\" " + + "[InvalidSafetyCenterConfig]\n1 errors, 0 warnings" + ) + } + + @Test fun unrelatedFile_doesNotThrow() { lint().files(xml("res/raw/some_other_config.xml", "<some-other-root/>")).run().expectClean() } @@ -121,15 +156,15 @@ class ParserExceptionDetectorTest : LintDetectorTest() { } private companion object { - val STRINGS_WITH_REFERENCE_XML: TestFile = - xml( - "res/values/strings.xml", - """ + const val STRINGS_WITH_REFERENCE_XML = + """ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="reference" translatable="false">Reference</string> </resources> """ - ) + + val STRINGS_WITH_REFERENCE_XML_DEFAULT: TestFile = + xml("res/values/strings.xml", STRINGS_WITH_REFERENCE_XML) const val VALID_TIRAMISU_CONFIG = """ |