Merge "37001: Lint check: non existing Activity/Service/Receiver"
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
index ac09a1b..8915e81 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -54,7 +54,7 @@
private static final List<Issue> sIssues;
static {
- final int initialCapacity = 106;
+ final int initialCapacity = 109;
List<Issue> issues = new ArrayList<Issue>(initialCapacity);
issues.add(AccessibilityDetector.ISSUE);
@@ -89,6 +89,9 @@
issues.add(LocaleDetector.STRING_LOCALE);
issues.add(LocaleDetector.DATE_FORMAT);
issues.add(RegistrationDetector.ISSUE);
+ issues.add(MissingClassDetector.MISSING);
+ issues.add(MissingClassDetector.INSTANTIATABLE);
+ issues.add(MissingClassDetector.INNERCLASS);
issues.add(HandlerDetector.ISSUE);
issues.add(FragmentDetector.ISSUE);
issues.add(TranslationDetector.EXTRA);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MissingClassDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MissingClassDetector.java
new file mode 100644
index 0000000..8c80b68
--- /dev/null
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MissingClassDetector.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2012 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.tools.lint.checks;
+
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_PKG_PREFIX;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_NAME;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_PACKAGE;
+import static com.android.tools.lint.detector.api.LintConstants.CONSTRUCTOR_NAME;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_ACTIVITY;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_APPLICATION;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_PROVIDER;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_RECEIVER;
+import static com.android.tools.lint.detector.api.LintConstants.TAG_SERVICE;
+
+import com.android.annotations.NonNull;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.ClassContext;
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.Detector.ClassScanner;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.LintUtils;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Location.Handle;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.Speed;
+import com.android.tools.lint.detector.api.XmlContext;
+import com.google.common.collect.Maps;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Checks to ensure that classes referenced in the manifest actually exist and are included
+ *
+ */
+public class MissingClassDetector extends LayoutDetector implements ClassScanner {
+ /** Manifest-referenced classes missing from the project or libraries */
+ public static final Issue MISSING = Issue.create(
+ "MissingRegistered", //$NON-NLS-1$
+ "Ensures that classes referenced in the manifest are present in the project or libraries",
+
+ "If a class is referenced in the manifest, it must also exist in the project (or in one " +
+ "of the libraries included by the project. This check helps uncover typos in " +
+ "registration names, or attempts to rename or move classes without updating the " +
+ "manifest file properly.",
+
+ Category.CORRECTNESS,
+ 8,
+ Severity.ERROR,
+ MissingClassDetector.class,
+ EnumSet.of(Scope.MANIFEST, Scope.CLASS_FILE, Scope.JAVA_LIBRARIES)).setMoreInfo(
+ "http://developer.android.com/guide/topics/manifest/manifest-intro.html"); //$NON-NLS-1$
+
+ /** Are activity, service, receiver etc subclasses instantiatable? */
+ public static final Issue INSTANTIATABLE = Issue.create(
+ "Instantiatable", //$NON-NLS-1$
+ "Ensures that classes registered in the manifest file are instantiatable",
+
+ "Activities, services, broadcast receivers etc. registered in the manifest file " +
+ "must be \"instiantable\" by the system, which means that the class must be " +
+ "public, it must have an empty public constructor, and if it's an inner class, " +
+ "it must be a static inner class.",
+
+ Category.CORRECTNESS,
+ 6,
+ Severity.WARNING,
+ MissingClassDetector.class,
+ Scope.CLASS_FILE_SCOPE);
+
+ /** Is the right character used for inner class separators? */
+ public static final Issue INNERCLASS = Issue.create(
+ "InnerclassSeparator", //$NON-NLS-1$
+ "Ensures that inner classes are referenced using '$' instead of '.' in class names",
+
+ "When you reference an inner class in a manifest file, you must use '$' instead of '.' " +
+ "as the separator character, e.g. Outer$Inner instead of Outer.Inner.\n" +
+ "\n" +
+ "(If you get this warning for a class which is not actually an inner class, it's " +
+ "because you are using uppercase characters in your package name, which is not " +
+ "conventional.)",
+
+ Category.CORRECTNESS,
+ 3,
+ Severity.WARNING,
+ MissingClassDetector.class,
+ Scope.MANIFEST_SCOPE);
+
+ private Map<String, Location.Handle> mReferencedClasses;
+
+ /** Constructs a new {@link MissingClassDetector} */
+ public MissingClassDetector() {
+ }
+
+ @Override
+ public @NonNull Speed getSpeed() {
+ return Speed.FAST;
+ }
+
+ // ---- Implements XmlScanner ----
+
+ @Override
+ public Collection<String> getApplicableElements() {
+ return Arrays.asList(
+ TAG_APPLICATION,
+ TAG_ACTIVITY,
+ TAG_SERVICE,
+ TAG_RECEIVER,
+ TAG_PROVIDER
+ );
+ }
+
+ @Override
+ public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
+
+ Element root = element.getOwnerDocument().getDocumentElement();
+ Attr classNameNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME);
+ if (classNameNode == null) {
+ return;
+ }
+ String className = classNameNode.getValue();
+ if (className.isEmpty()) {
+ return;
+ }
+
+ String pkg = root.getAttribute(ATTR_PACKAGE);
+ String fqcn;
+ if (className.startsWith(".")) { //$NON-NLS-1$
+ fqcn = pkg + className;
+ } else if (className.indexOf('.') == -1) {
+ // According to the <activity> manifest element documentation, this is not
+ // valid ( http://developer.android.com/guide/topics/manifest/activity-element.html )
+ // but it appears in manifest files and appears to be supported by the runtime
+ // so handle this in code as well:
+ fqcn = pkg + '.' + className;
+ } else { // else: the class name is already a fully qualified class name
+ fqcn = className;
+ }
+
+ String signature = ClassContext.getInternalName(fqcn);
+ if (signature.isEmpty() || signature.startsWith(ANDROID_PKG_PREFIX)) {
+ return;
+ }
+
+ if (mReferencedClasses == null) {
+ mReferencedClasses = Maps.newHashMapWithExpectedSize(16);
+ }
+
+ Handle handle = context.parser.createLocationHandle(context, element);
+ mReferencedClasses.put(signature, handle);
+
+ if (signature.indexOf('$') != -1) {
+ if (className.indexOf('$') == -1 && className.indexOf('.', 1) > 0) {
+ boolean haveUpperCase = false;
+ for (int i = 0, n = pkg.length(); i < n; i++) {
+ if (Character.isUpperCase(pkg.charAt(i))) {
+ haveUpperCase = true;
+ break;
+ }
+ }
+ if (!haveUpperCase) {
+ String message = String.format("Use '$' instead of '.' for inner classes " +
+ "(or use only lowercase letters in package names)", className);
+ Location location = context.getLocation(classNameNode);
+ context.report(INNERCLASS, element, location, message, null);
+ }
+ }
+
+ // The internal name contains a $ which means it's an inner class.
+ // The conversion from fqcn to internal name is a bit ambiguous:
+ // "a.b.C.D" usually means "inner class D in class C in package a.b".
+ // However, it can (see issue 31592) also mean class D in package "a.b.C".
+ // To make sure we don't falsely complain that foo/Bar$Baz doesn't exist,
+ // in case the user has actually created a package named foo/Bar and a proper
+ // class named Baz, we register *both* into the reference map.
+ // When generating errors we'll look for these an rip them back out if
+ // it looks like one of the two variations have been seen.
+ signature = signature.replace('$', '/');
+ mReferencedClasses.put(signature, handle);
+ }
+ }
+
+ @Override
+ public void afterCheckProject(@NonNull Context context) {
+ if (!context.getProject().isLibrary() && mReferencedClasses != null &&
+ !mReferencedClasses.isEmpty()) {
+ List<String> classes = new ArrayList<String>(mReferencedClasses.keySet());
+ Collections.sort(classes);
+ for (String owner : classes) {
+ Location.Handle handle = mReferencedClasses.get(owner);
+ String fqcn = ClassContext.getFqcn(owner);
+
+ String signature = ClassContext.getInternalName(fqcn);
+ if (!signature.equals(owner)) {
+ if (!mReferencedClasses.containsKey(signature)) {
+ continue;
+ }
+ } else {
+ signature = signature.replace('$', '/');
+ if (!mReferencedClasses.containsKey(signature)) {
+ continue;
+ }
+ }
+ mReferencedClasses.remove(owner);
+
+ String message = String.format(
+ "Class referenced in the manifest, %1$s, was not found in the " +
+ "project or the libraries", fqcn);
+ Location location = handle.resolve();
+ context.report(MISSING, location, message, null);
+ }
+ }
+ }
+
+ // ---- Implements ClassScanner ----
+
+ @Override
+ public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) {
+ String curr = classNode.name;
+ if (mReferencedClasses != null && mReferencedClasses.containsKey(curr)) {
+ mReferencedClasses.remove(curr);
+
+ // Ensure that the class is public, non static and has a null constructor!
+
+ if ((classNode.access & Opcodes.ACC_PUBLIC) == 0) {
+ context.report(INSTANTIATABLE, context.getLocation(classNode), String.format(
+ "This class should be public (%1$s)",
+ ClassContext.createSignature(classNode.name, null, null)),
+ null);
+ return;
+ }
+
+ if (classNode.name.indexOf('$') != -1 && !LintUtils.isStaticInnerClass(classNode)) {
+ context.report(INSTANTIATABLE, context.getLocation(classNode), String.format(
+ "This inner class should be static (%1$s)",
+ ClassContext.createSignature(classNode.name, null, null)),
+ null);
+ return;
+ }
+
+ boolean hasDefaultConstructor = false;
+ @SuppressWarnings("rawtypes") // ASM API
+ List methodList = classNode.methods;
+ for (Object m : methodList) {
+ MethodNode method = (MethodNode) m;
+ if (method.name.equals(CONSTRUCTOR_NAME)) {
+ if (method.desc.equals("()V")) { //$NON-NLS-1$
+ // The constructor must be public
+ if ((method.access & Opcodes.ACC_PUBLIC) != 0) {
+ hasDefaultConstructor = true;
+ } else {
+ context.report(INSTANTIATABLE, context.getLocation(method, classNode),
+ "The default constructor must be public",
+ null);
+ // Also mark that we have a constructor so we don't complain again
+ // below since we've already emitted a more specific error related
+ // to the default constructor
+ hasDefaultConstructor = true;
+ }
+ }
+ }
+ }
+
+ if (!hasDefaultConstructor) {
+ context.report(INSTANTIATABLE, context.getLocation(classNode), String.format(
+ "This class should provide a default constructor (a public " +
+ "constructor with no arguments) (%1$s)",
+ ClassContext.createSignature(classNode.name, null, null)),
+ null);
+ }
+ }
+ }
+}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/RegistrationDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/RegistrationDetector.java
index b645253..5f4aff4 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/RegistrationDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/RegistrationDetector.java
@@ -117,23 +117,29 @@
}
}
- private static String getFqcn(Element element) {
- StringBuilder sb = new StringBuilder();
+ /**
+ * Returns the fully qualified class name for a manifest entry element that
+ * specifies a name attribute
+ *
+ * @param element the element
+ * @return the fully qualified class name
+ */
+ @NonNull
+ private static String getFqcn(@NonNull Element element) {
Element root = element.getOwnerDocument().getDocumentElement();
String pkg = root.getAttribute(ATTR_PACKAGE);
String className = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
if (className.startsWith(".")) { //$NON-NLS-1$
- sb.append(pkg);
+ return pkg + className;
} else if (className.indexOf('.') == -1) {
// According to the <activity> manifest element documentation, this is not
// valid ( http://developer.android.com/guide/topics/manifest/activity-element.html )
// but it appears in manifest files and appears to be supported by the runtime
// so handle this in code as well:
- sb.append(pkg);
- sb.append('.');
+ return pkg + '.' + className;
} // else: the class name is already a fully qualified class name
- sb.append(className);
- return sb.toString();
+
+ return className;
}
// ---- Implements ClassScanner ----
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
index d644951..de5b65e 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
@@ -55,9 +55,19 @@
public abstract class AbstractCheckTest extends TestCase {
protected abstract Detector getDetector();
+ private Detector mDetector;
+
+ private Detector getDetectorInstance() {
+ if (mDetector == null) {
+ mDetector = getDetector();
+ }
+
+ return mDetector;
+ }
+
protected List<Issue> getIssues() {
List<Issue> issues = new ArrayList<Issue>();
- Class<? extends Detector> detectorClass = getDetector().getClass();
+ Class<? extends Detector> detectorClass = getDetectorInstance().getClass();
// Get the list of issues from the registry and filter out others, to make sure
// issues are properly registered
List<Issue> candidates = new BuiltinIssueRegistry().getIssues();
@@ -264,7 +274,7 @@
}
protected boolean isEnabled(Issue issue) {
- Class<? extends Detector> detectorClass = getDetector().getClass();
+ Class<? extends Detector> detectorClass = getDetectorInstance().getClass();
if (issue.getDetectorClass() == detectorClass) {
return true;
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/MissingClassDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/MissingClassDetectorTest.java
new file mode 100644
index 0000000..d8f18b6
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/MissingClassDetectorTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2012 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+import java.io.File;
+import java.util.Arrays;
+
+@SuppressWarnings("javadoc")
+public class MissingClassDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new MissingClassDetector();
+ }
+
+ public void test() throws Exception {
+ assertEquals(
+ "AndroidManifest.xml:13: Error: Class referenced in the manifest, test.pkg.TestProvider, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <activity android:name=\".TestProvider\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.TestProvider2, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <service android:name=\"test.pkg.TestProvider2\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:15: Error: Class referenced in the manifest, test.pkg.TestService, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <provider android:name=\".TestService\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:16: Error: Class referenced in the manifest, test.pkg.OnClickActivity, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <receiver android:name=\"OnClickActivity\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:17: Error: Class referenced in the manifest, test.pkg.TestReceiver, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <service android:name=\"TestReceiver\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "5 errors, 0 warnings\n",
+
+ lintProject(
+ "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath"
+ ));
+ }
+
+ public void testOkClasses() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath",
+ "bytecode/OnClickActivity.java.txt=>src/test/pkg/OnClickActivity.java",
+ "bytecode/OnClickActivity.class.data=>bin/classes/test/pkg/OnClickActivity.class",
+ "bytecode/TestService.java.txt=>src/test/pkg/TestService.java",
+ "bytecode/TestService.class.data=>bin/classes/test/pkg/TestService.class",
+ "bytecode/TestProvider.java.txt=>src/test/pkg/TestProvider.java",
+ "bytecode/TestProvider.class.data=>bin/classes/test/pkg/TestProvider.class",
+ "bytecode/TestProvider2.java.txt=>src/test/pkg/TestProvider2.java",
+ "bytecode/TestProvider2.class.data=>bin/classes/test/pkg/TestProvider2.class",
+ "bytecode/TestReceiver.java.txt=>src/test/pkg/TestReceiver.java",
+ "bytecode/TestReceiver.class.data=>bin/classes/test/pkg/TestReceiver.class"
+ ));
+ }
+
+ public void testOkLibraries() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath",
+ "bytecode/classes.jar=>libs/classes.jar"
+ ));
+ }
+
+ public void testLibraryProjects() throws Exception {
+ File master = getProjectDir("MasterProject",
+ // Master project
+ "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml",
+ "multiproject/main.properties=>project.properties",
+ "bytecode/.classpath=>.classpath"
+ );
+ File library = getProjectDir("LibraryProject",
+ // Library project
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "bytecode/OnClickActivity.java.txt=>src/test/pkg/OnClickActivity.java",
+ "bytecode/OnClickActivity.class.data=>bin/classes/test/pkg/OnClickActivity.class",
+ "bytecode/TestService.java.txt=>src/test/pkg/TestService.java",
+ "bytecode/TestService.class.data=>bin/classes/test/pkg/TestService.class",
+ "bytecode/TestProvider.java.txt=>src/test/pkg/TestProvider.java",
+ "bytecode/TestProvider.class.data=>bin/classes/test/pkg/TestProvider.class",
+ "bytecode/TestProvider2.java.txt=>src/test/pkg/TestProvider2.java",
+ "bytecode/TestProvider2.class.data=>bin/classes/test/pkg/TestProvider2.class"
+ // Missing TestReceiver: Test should complain about just that class
+ );
+ assertEquals(
+ "MasterProject/AndroidManifest.xml:17: Error: Class referenced in the manifest, test.pkg.TestReceiver, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <service android:name=\"TestReceiver\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ checkLint(Arrays.asList(master, library)));
+ }
+
+ public void testInnerClassStatic() throws Exception {
+ assertEquals(
+ "src/test/pkg/Foo.java:8: Warning: This inner class should be static (test.pkg.Foo.Baz) [Instantiatable]\n" +
+ " public class Baz extends Activity {\n" +
+ " ^\n" +
+ "0 errors, 1 warnings\n",
+
+ lintProject(
+ "registration/AndroidManifest.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath",
+ "registration/Foo.java.txt=>src/test/pkg/Foo.java",
+ "registration/Foo.class.data=>bin/classes/test/pkg/Foo.class",
+ "registration/Foo$Bar.class.data=>bin/classes/test/pkg/Foo$Bar.class",
+ "registration/Foo$Baz.class.data=>bin/classes/test/pkg/Foo$Baz.class"
+ ));
+ }
+
+ public void testInnerClassPublic() throws Exception {
+ assertEquals(
+ "src/test/pkg/Foo/Bar.java:6: Warning: The default constructor must be public [Instantiatable]\n" +
+ " private Bar() {\n" +
+ " ^\n" +
+ "0 errors, 1 warnings\n",
+
+ lintProject(
+ "registration/AndroidManifestInner.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath",
+ "registration/Bar.java.txt=>src/test/pkg/Foo/Bar.java",
+ "registration/Bar.class.data=>bin/classes/test/pkg/Foo/Bar.class"
+ ));
+ }
+
+ public void testInnerClass() throws Exception {
+ assertEquals(
+ "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <activity\n" +
+ " ^\n" +
+ "AndroidManifest.xml:23: Error: Class referenced in the manifest, test.pkg.Foo.Baz, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <activity\n" +
+ " ^\n" +
+ "2 errors, 0 warnings\n",
+
+ lintProject(
+ "registration/AndroidManifest.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath",
+ "registration/Foo.java.txt=>src/test/pkg/Foo.java"
+ ));
+ }
+
+ public void testInnerClass2() throws Exception {
+ assertEquals(
+ "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <activity\n" +
+ " ^\n" +
+ "1 errors, 0 warnings\n",
+
+ lintProject(
+ "registration/AndroidManifestInner.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath",
+ "registration/Bar.java.txt=>src/test/pkg/Foo/Bar.java"
+ ));
+ }
+
+ public void testWrongSeparator1() throws Exception {
+ assertEquals(
+ "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <activity\n" +
+ " ^\n" +
+ "1 errors, 0 warnings\n",
+
+ lintProject(
+ "registration/AndroidManifestWrong.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath",
+ "registration/Bar.java.txt=>src/test/pkg/Foo/Bar.java"
+ ));
+ }
+
+ public void testWrongSeparator2() throws Exception {
+ assertEquals(
+ "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" +
+ " <activity\n" +
+ " ^\n" +
+ "AndroidManifest.xml:15: Warning: Use '$' instead of '.' for inner classes (or use only lowercase letters in package names) [InnerclassSeparator]\n" +
+ " android:name=\".Foo.Bar\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 1 warnings\n",
+
+ lintProject(
+ "registration/AndroidManifestWrong2.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath",
+ "registration/Bar.java.txt=>src/test/pkg/Foo/Bar.java"
+ ));
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classes.jar b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classes.jar
new file mode 100644
index 0000000..fa52dcf
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classes.jar
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifest.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifest.xml
new file mode 100644
index 0000000..0b07cdf
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.pkg"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="16" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name=".Foo$Bar"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".Foo$Baz"
+ android:label="@string/app_name" >
+ </activity>
+ </application>
+
+</manifest>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml
new file mode 100644
index 0000000..1b87d9d
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml
@@ -0,0 +1,25 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.pkg.Foo"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="16" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name=".Bar"
+ android:label="@string/app_name" >
+ <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/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml
new file mode 100644
index 0000000..bfdcde0
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml
@@ -0,0 +1,25 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.pkg.Foo"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="16" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="test.pkg.Foo.Bar"
+ android:label="@string/app_name" >
+ <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/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml
new file mode 100644
index 0000000..8773e0f
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml
@@ -0,0 +1,25 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.pkg"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="16" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name=".Foo.Bar"
+ android:label="@string/app_name" >
+ <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/tests/src/com/android/tools/lint/checks/data/registration/Bar.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.class.data
new file mode 100644
index 0000000..e51a0e8
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.java.txt
new file mode 100644
index 0000000..b514f11
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.java.txt
@@ -0,0 +1,8 @@
+package test.pkg.Foo;
+
+import android.app.Activity;
+
+public class Bar extends Activity {
+ private Bar() {
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data
new file mode 100644
index 0000000..da45ebb
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data
new file mode 100644
index 0000000..aadd767
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.class.data
new file mode 100644
index 0000000..aafcc73
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.java.txt
new file mode 100644
index 0000000..a647030
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.java.txt
@@ -0,0 +1,10 @@
+package test.pkg;
+
+import android.app.Activity;
+
+public class Foo {
+ public static class Bar extends Activity {
+ }
+ public class Baz extends Activity {
+ }
+}