diff options
5 files changed, 161 insertions, 4 deletions
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestOrderDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestOrderDetectorTest.java index 8508d40..e845b57 100644 --- a/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestOrderDetectorTest.java +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestOrderDetectorTest.java @@ -287,4 +287,36 @@ public class ManifestOrderDetectorTest extends AbstractCheckTest { checkLint(Arrays.asList(master, library))); } + public void testMissingVersion() throws Exception { + mEnabled = Collections.singleton(ManifestOrderDetector.SET_VERSION); + assertEquals("" + + "AndroidManifest.xml:2: Warning: Should set android:versionCode to specify the application version [MissingVersion]\n" + + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + "^\n" + + "AndroidManifest.xml:2: Warning: Should set android:versionName to specify the application version [MissingVersion]\n" + + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + "^\n" + + "0 errors, 2 warnings\n", + lintProject("no_version.xml=>AndroidManifest.xml")); + } + + public void testIllegalReference() throws Exception { + mEnabled = Collections.singleton(ManifestOrderDetector.ILLEGAL_REFERENCE); + assertEquals("" + + "AndroidManifest.xml:2: Warning: The android:versionCode cannot be a resource url, it must be a literal integer [IllegalResourceRef]\n" + + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + "^\n" + + "AndroidManifest.xml:2: Warning: The android:versionName cannot be a resource url, it must be a literal string [IllegalResourceRef]\n" + + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + "^\n" + + "AndroidManifest.xml:7: Warning: The android:minSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n" + + " <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "AndroidManifest.xml:7: Warning: The android:targetSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n" + + " <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 4 warnings\n", + + lintProject("illegal_version.xml=>AndroidManifest.xml")); + } } diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml new file mode 100644 index 0000000..3028117 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" + android:versionCode="@dimen/versionCode" + android:versionName="@dimen/versionName" > + + <uses-sdk android:minSdkVersion="@dimen/minSdkVersion" android:targetSdkVersion="@dimen/targetSdkVersion" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/no_version.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/no_version.xml new file mode 100644 index 0000000..1c3c0a4 --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/no_version.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="foo.bar2" > + + <uses-sdk android:minSdkVersion="14" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <activity + android:label="@string/app_name" + android:name=".Foo2Activity" > + <intent-filter > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java index 71866c3..b1ce430 100644 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java +++ b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java @@ -55,7 +55,7 @@ public class BuiltinIssueRegistry extends IssueRegistry { private static final List<Issue> sIssues; static { - final int initialCapacity = 134; + final int initialCapacity = 136; List<Issue> issues = new ArrayList<Issue>(initialCapacity); issues.add(AccessibilityDetector.ISSUE); @@ -132,6 +132,8 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(ManifestOrderDetector.TARGET_NEWER); issues.add(ManifestOrderDetector.ALLOW_BACKUP); issues.add(ManifestOrderDetector.UNIQUE_PERMISSION); + issues.add(ManifestOrderDetector.SET_VERSION); + issues.add(ManifestOrderDetector.ILLEGAL_REFERENCE); issues.add(SecurityDetector.EXPORTED_PROVIDER); issues.add(SecurityDetector.EXPORTED_SERVICE); issues.add(SecurityDetector.EXPORTED_ACTIVITY); diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ManifestOrderDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ManifestOrderDetector.java index 9762c07..83fac97 100644 --- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ManifestOrderDetector.java +++ b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/ManifestOrderDetector.java @@ -22,6 +22,7 @@ import static com.android.SdkConstants.ATTR_MIN_SDK_VERSION; import static com.android.SdkConstants.ATTR_NAME; import static com.android.SdkConstants.ATTR_PACKAGE; import static com.android.SdkConstants.ATTR_TARGET_SDK_VERSION; +import static com.android.SdkConstants.PREFIX_RESOURCE_REF; import static com.android.SdkConstants.TAG_ACTIVITY; import static com.android.SdkConstants.TAG_APPLICATION; import static com.android.SdkConstants.TAG_PERMISSION; @@ -203,7 +204,7 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann Severity.WARNING, ManifestOrderDetector.class, Scope.MANIFEST_SCOPE).setMoreInfo( - "http://developer.android.com/reference/android/R.attr.html#allowBackup"); + "http://developer.android.com/reference/android/R.attr.html#allowBackup"); /** Conflicting permission names */ public static final Issue UNIQUE_PERMISSION = Issue.create( @@ -225,6 +226,40 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann ManifestOrderDetector.class, Scope.MANIFEST_SCOPE); + /** Using a resource for attributes that do not allow it */ + public static final Issue SET_VERSION = Issue.create( + "MissingVersion", //$NON-NLS-1$ + "Checks that the application name and version are set", + + "You should define the version information for your application.\n" + + "`android:versionCode`: An integer value that represents the version of the " + + "application code, relative to other versions.\n" + + "\n" + + "`android:versionName`: A string value that represents the release version of " + + "the application code, as it should be shown to users.", + + Category.CORRECTNESS, + 2, + Severity.WARNING, + ManifestOrderDetector.class, + Scope.MANIFEST_SCOPE).setMoreInfo( + "http://developer.android.com/tools/publishing/versioning.html#appversioning"); + + /** Using a resource for attributes that do not allow it */ + public static final Issue ILLEGAL_REFERENCE = Issue.create( + "IllegalResourceRef", //$NON-NLS-1$ + "Checks for resource references where only literals are allowed", + + "For the `versionCode` attribute, you have to specify an actual integer " + + "literal; you cannot use an indirection with a `@dimen/name` resource. " + + "Similarly, the `versionName` attribute should be an actual string, not " + + "a string resource url.", + + Category.CORRECTNESS, + 8, + Severity.WARNING, + ManifestOrderDetector.class, + Scope.MANIFEST_SCOPE); /** Constructs a new {@link ManifestOrderDetector} check */ public ManifestOrderDetector() { @@ -263,6 +298,12 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann @Override public void afterCheckFile(@NonNull Context context) { + XmlContext xmlContext = (XmlContext) context; + Element element = xmlContext.document.getDocumentElement(); + if (element != null) { + checkDocumentElement(xmlContext, element); + } + if (mSeenUsesSdk == 0 && context.isEnabled(USES_SDK)) { context.report(USES_SDK, Location.create(context.file), "Manifest should specify a minimum API level with " + @@ -271,6 +312,29 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann } } + private void checkDocumentElement(XmlContext context, Element element) { + Attr codeNode = element.getAttributeNodeNS(ANDROID_URI, "versionCode");//$NON-NLS-1$ + if (codeNode != null && codeNode.getValue().startsWith(PREFIX_RESOURCE_REF) + && context.isEnabled(ILLEGAL_REFERENCE)) { + context.report(ILLEGAL_REFERENCE, element, context.getLocation(element), + "The android:versionCode cannot be a resource url, it must be " + + "a literal integer", null); + } else if (codeNode == null && context.isEnabled(SET_VERSION)) { + context.report(SET_VERSION, element, context.getLocation(element), + "Should set android:versionCode to specify the application version", null); + } + Attr nameNode = element.getAttributeNodeNS(ANDROID_URI, "versionName");//$NON-NLS-1$ + if (nameNode != null && nameNode.getValue().startsWith(PREFIX_RESOURCE_REF) + && context.isEnabled(ILLEGAL_REFERENCE)) { + context.report(ILLEGAL_REFERENCE, element, context.getLocation(element), + "The android:versionName cannot be a resource url, it must be " + + "a literal string", null); + } else if (nameNode == null && context.isEnabled(SET_VERSION)) { + context.report(SET_VERSION, element, context.getLocation(element), + "Should set android:versionName to specify the application version", null); + } + } + // ---- Implements Detector.XmlScanner ---- @Override @@ -377,6 +441,14 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann "<uses-sdk> tag should specify a minimum API level with " + "android:minSdkVersion=\"?\"", null); } + } else { + Attr codeNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_MIN_SDK_VERSION); + if (codeNode != null && codeNode.getValue().startsWith(PREFIX_RESOURCE_REF) + && context.isEnabled(ILLEGAL_REFERENCE)) { + context.report(ILLEGAL_REFERENCE, element, context.getLocation(element), + "The android:minSdkVersion cannot be a resource url, it must be " + + "a literal integer (or string if a preview codename)", null); + } } if (!element.hasAttributeNS(ANDROID_URI, ATTR_TARGET_SDK_VERSION)) { @@ -405,8 +477,15 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann // Ignore: AAPT will enforce this. } } - } + Attr nameNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_TARGET_SDK_VERSION); + if (nameNode != null && nameNode.getValue().startsWith(PREFIX_RESOURCE_REF) + && context.isEnabled(ILLEGAL_REFERENCE)) { + context.report(ILLEGAL_REFERENCE, element, context.getLocation(element), + "The android:targetSdkVersion cannot be a resource url, it must be " + + "a literal integer (or string if a preview codename)", null); + } + } if (tag.equals(TAG_PERMISSION)) { Attr nameNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); if (nameNode != null) { @@ -470,7 +549,7 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann private String getPackage(Element element) { if (mPackage == null) { - return element.getOwnerDocument().getDocumentElement().getAttribute(ATTR_PACKAGE); + mPackage = element.getOwnerDocument().getDocumentElement().getAttribute(ATTR_PACKAGE); } return mPackage; |