aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2012-07-19 17:57:30 -0700
committerTor Norbye <tnorbye@google.com>2012-07-23 15:06:58 -0700
commit2c611aed27d75bd816b7887b64f98ff1ed87b097 (patch)
treef73f0d4c49d4eda4c48b8266bf0bffab2a81b06a
parent4ca1c4d2c780b345854b9824963cd6cf930a7276 (diff)
downloadsdk-2c611aed27d75bd816b7887b64f98ff1ed87b097.zip
sdk-2c611aed27d75bd816b7887b64f98ff1ed87b097.tar.gz
sdk-2c611aed27d75bd816b7887b64f98ff1ed87b097.tar.bz2
Add lint checks for wakelock and secure random problems
This changeset adds new two lint bytecode-based detectors. The WakelockDetector looks for problems with wakelocks: - Calling release() in onDestroy() rather than in onPause() - Calling acquire() but never calling release() anywhere in the app. The SecureRandomDetector looks for problems with the SecureRandom random number generator: - Calling setSeed() with a fixed number, such as a string literal, or something equivalent (such as a static field reference of this or some other class) - Calling setSeed() and passing in System.nanoTime or currentTimeMillis since these are considered predictable seeds. These are both using the new fast-dispatch mechanism for bytecode detectors in lint. In both cases, there are more complex control flow we should be checking using flow analysis; that's for an upcoming CL where we add ASM's analysis library. Change-Id: Iec2a95b042b8a3e4e976923cc62e9dccf2dfeca5
-rw-r--r--eclipse/dictionary.txt2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java4
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/SecureRandomDetector.java122
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/WakelockDetector.java133
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecureRandomDetectorTest.java49
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/WakelockDetectorTest.java53
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.databin0 -> 1379 bytes
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt42
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.databin0 -> 920 bytes
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt17
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.databin0 -> 779 bytes
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt24
12 files changed, 445 insertions, 1 deletions
diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt
index 1985473..1cc5443 100644
--- a/eclipse/dictionary.txt
+++ b/eclipse/dictionary.txt
@@ -331,6 +331,8 @@ varargs
verbosity
viewport
vs
+wakelock
+wakelocks
wallpaper
webtools
whilst
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 2ad7f12..1dec5f6 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 @@ public class BuiltinIssueRegistry extends IssueRegistry {
private static final List<Issue> sIssues;
static {
- final int initialCapacity = 98;
+ final int initialCapacity = 100;
List<Issue> issues = new ArrayList<Issue>(initialCapacity);
issues.add(AccessibilityDetector.ISSUE);
@@ -116,6 +116,7 @@ public class BuiltinIssueRegistry extends IssueRegistry {
issues.add(SecurityDetector.OPEN_PROVIDER);
issues.add(SecurityDetector.WORLD_READABLE);
issues.add(SecurityDetector.WORLD_WRITEABLE);
+ issues.add(SecureRandomDetector.ISSUE);
issues.add(IconDetector.GIF_USAGE);
issues.add(IconDetector.ICON_DENSITIES);
issues.add(IconDetector.ICON_MISSING_FOLDER);
@@ -150,6 +151,7 @@ public class BuiltinIssueRegistry extends IssueRegistry {
issues.add(JavaPerformanceDetector.PAINT_ALLOC);
issues.add(JavaPerformanceDetector.USE_VALUEOF);
issues.add(JavaPerformanceDetector.USE_SPARSEARRAY);
+ issues.add(WakelockDetector.ISSUE);
issues.add(SetJavaScriptEnabledDetector.ISSUE);
issues.add(ToastDetector.ISSUE);
issues.add(SharedPrefsDetector.ISSUE);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecureRandomDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecureRandomDetector.java
new file mode 100644
index 0000000..79f5d47
--- /dev/null
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecureRandomDetector.java
@@ -0,0 +1,122 @@
+/*
+ * 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.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.ClassContext;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.ClassScanner;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LintUtils;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.sun.xml.internal.ws.org.objectweb.asm.Opcodes;
+
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Checks for hardcoded seeds with random numbers.
+ */
+public class SecureRandomDetector extends Detector implements ClassScanner {
+ /** Unregistered activities and services */
+ public static final Issue ISSUE = Issue.create(
+ "SecureRandom", //$NON-NLS-1$
+ "Looks for suspicious usage of the SecureRandom class",
+
+ "Specifying a fixed seed will cause the instance to return a predictable sequence " +
+ "of numbers. This may be useful for testing but it is not appropriate for secure use.",
+
+ Category.PERFORMANCE,
+ 9,
+ Severity.WARNING,
+ SecureRandomDetector.class,
+ Scope.CLASS_FILE_SCOPE).
+ setMoreInfo("http://developer.android.com/reference/java/security/SecureRandom.html");
+
+ private static final String SET_SEED = "setSeed"; //$NON-NLS-1$
+ private static final String OWNER_SECURE_RANDOM = "java/security/SecureRandom"; //$NON-NLS-1$
+ private static final String OWNER_RANDOM = "java/util/Random"; //$NON-NLS-1$
+ /** Method description for a method that takes a long argument (no return type specified */
+ private static final String LONG_ARG = "(J)"; //$NON-NLS-1$
+
+ /** Constructs a new {@link SecureRandomDetector} */
+ public SecureRandomDetector() {
+ }
+
+ // ---- Implements ClassScanner ----
+
+ @Override
+ @Nullable
+ public List<String> getApplicableCallNames() {
+ return Arrays.asList(SET_SEED);
+ }
+
+ @Override
+ public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode,
+ @NonNull MethodNode method, @NonNull MethodInsnNode call) {
+ String owner = call.owner;
+ String desc = call.desc;
+ if (owner.equals(OWNER_SECURE_RANDOM)) {
+ if (desc.startsWith(LONG_ARG)) {
+ checkValidSetSeed(context, call);
+ } else if (desc.startsWith("([B)")) { //$NON-NLS-1$
+ // setSeed(byte[]) ...
+ // We could do some flow analysis here to see whether the byte array getting
+ // passed in appears to be fixed.
+ // However, people calling this constructor rather than the simpler one
+ // with a fixed integer are probably less likely to make that mistake... right?
+ }
+ } else if (owner.equals(OWNER_RANDOM) && desc.startsWith(LONG_ARG)) {
+ // Called setSeed(long) on an instanceof a Random object. Flag this if the instance
+ // is likely a SecureRandom.
+ // TODO
+ }
+ }
+
+ private void checkValidSetSeed(ClassContext context, MethodInsnNode call) {
+ assert call.name.equals(SET_SEED);
+
+ // Make sure the argument passed is not a literal
+ AbstractInsnNode prev = LintUtils.getPrevInstruction(call);
+ if (prev == null) {
+ return;
+ }
+ int opcode = prev.getOpcode();
+ if (opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 || opcode == Opcodes.LDC) {
+ context.report(ISSUE, context.getLocation(call),
+ "Do not call setSeed() on a SecureRandom with a fixed seed: " +
+ "it is not secure. Use getSeed().",
+ null);
+ } else if (opcode == Opcodes.INVOKESTATIC) {
+ String methodName = ((MethodInsnNode) prev).name;
+ if (methodName.equals("currentTimeMillis") || methodName.equals("nanoTime")) {
+ context.report(ISSUE, context.getLocation(call),
+ "It is dangerous to seed SecureRandom with the current time because " +
+ "that value is more predictable to an attacker than the default seed.",
+ null);
+ }
+ }
+ }
+}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WakelockDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WakelockDetector.java
new file mode 100644
index 0000000..7ed0edf
--- /dev/null
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WakelockDetector.java
@@ -0,0 +1,133 @@
+/*
+ * 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_APP_ACTIVITY;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+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;
+import com.android.tools.lint.detector.api.Detector.ClassScanner;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Checks for problems with wakelocks (such as failing to release them)
+ * which can lead to unnecessary battery usage.
+ */
+public class WakelockDetector extends Detector implements ClassScanner {
+
+ /** Problems using wakelocks */
+ public static final Issue ISSUE = Issue.create(
+ "Wakelock", //$NON-NLS-1$
+ "Looks for problems with wakelock usage",
+
+ "Failing to release a wakelock properly can keep the Android device in " +
+ "a high power mode, which reduces battery life. There are several causes " +
+ "of this, such as releasing the wake lock in onDestroy() instead of in " +
+ "onPause(), failing to call release() in all possible code paths after " +
+ "an acquire(), and so on.\n" +
+ "\n" +
+ "NOTE: If you are using the lock just to keep the screen on, you should " +
+ "strongly consider using FLAG_KEEP_SCREEN_ON instead. This window flag " +
+ "will be correctly managed by the platform as the user moves between " +
+ "applications and doesn't require a special permission. See " +
+ "http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_KEEP_SCREEN_ON.",
+
+ Category.PERFORMANCE,
+ 9,
+ Severity.WARNING,
+ WakelockDetector.class,
+ Scope.CLASS_FILE_SCOPE);
+
+ private static final String WAKELOCK_OWNER = "android/os/PowerManager$WakeLock"; //$NON-NLS-1$
+ private static final String RELEASE_METHOD = "release"; //$NON-NLS-1$
+ private static final String ACQUIRE_METHOD = "acquire"; //$NON-NLS-1$
+
+ /** Constructs a new {@link WakelockDetector} */
+ public WakelockDetector() {
+ }
+
+ @Override
+ public void afterCheckProject(@NonNull Context context) {
+ if (mHasAcquire && !mHasRelease && context.getDriver().getPhase() == 1) {
+ // Gather positions of the acquire calls
+ context.getDriver().requestRepeat(this, Scope.CLASS_FILE_SCOPE);
+ }
+ }
+
+ // ---- Implements ClassScanner ----
+
+ /** Whether any {@code acquire()} calls have been encountered */
+ private boolean mHasAcquire;
+
+ /** Whether any {@code release()} calls have been encountered */
+ private boolean mHasRelease;
+
+ @Override
+ @Nullable
+ public List<String> getApplicableCallNames() {
+ return Arrays.asList(ACQUIRE_METHOD, RELEASE_METHOD);
+ }
+
+ @Override
+ public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode,
+ @NonNull MethodNode method, @NonNull MethodInsnNode call) {
+ if (call.owner.equals(WAKELOCK_OWNER)) {
+ String name = call.name;
+ if (name.equals(ACQUIRE_METHOD)) {
+ mHasAcquire = true;
+
+ if (context.getDriver().getPhase() == 2) {
+ assert !mHasRelease;
+ context.report(ISSUE, method, context.getLocation(call),
+ "Found a wakelock acquire() but no release() calls anywhere",
+ null);
+ } else {
+ assert context.getDriver().getPhase() == 1;
+ // Perform flow analysis in this method to see if we're
+ // performing an acquire/release block, where there are code paths
+ // between the acquire and release which can result in the
+ // release call not getting reached.
+ // TODO: Implement this.
+ }
+ } else if (name.equals(RELEASE_METHOD)) {
+ mHasRelease = true;
+
+ // See if the release is happening in an onDestroy method, in an
+ // activity.
+ if ("onDestroy".equals(method.name) //$NON-NLS-1$
+ && context.getDriver().isSubclassOf(
+ classNode, ANDROID_APP_ACTIVITY)) {
+ context.report(ISSUE, method, context.getLocation(call),
+ "Wakelocks should be released in onPause, not onDestroy",
+ null);
+ }
+ }
+ }
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecureRandomDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecureRandomDetectorTest.java
new file mode 100644
index 0000000..2fbdaad
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/SecureRandomDetectorTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+@SuppressWarnings("javadoc")
+public class SecureRandomDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new SecureRandomDetector();
+ }
+
+ public void test1() throws Exception {
+ assertEquals(
+ "SecureRandomTest.java:12: Warning: It is dangerous to seed SecureRandom with the current time because that value is more predictable to an attacker than the default seed.\n" +
+ "SecureRandomTest.java:14: Warning: Do not call setSeed() on a SecureRandom with a fixed seed: it is not secure. Use getSeed().\n" +
+ "SecureRandomTest.java:15: Warning: Do not call setSeed() on a SecureRandom with a fixed seed: it is not secure. Use getSeed().\n" +
+ "SecureRandomTest.java:16: Warning: Do not call setSeed() on a SecureRandom with a fixed seed: it is not secure. Use getSeed().\n" +
+ "SecureRandomTest.java:17: Warning: Do not call setSeed() on a SecureRandom with a fixed seed: it is not secure. Use getSeed().\n" +
+ "SecureRandomTest.java:18: Warning: Do not call setSeed() on a SecureRandom with a fixed seed: it is not secure. Use getSeed().",
+ // Missing errors on lines 28 and 40, using flow analysis to determine that
+ // setSeed on a random instance is really a call on a SecureRandom instance,
+ // and using flow analysis to determine that the seed byte array passed into
+ // the SecureRandom constructor is static.
+
+ lintProject(
+ "bytecode/.classpath=>.classpath",
+ "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
+ "res/layout/onclick.xml=>res/layout/onclick.xml",
+ "bytecode/SecureRandomTest.java.txt=>src/test/pkg/SecureRandomTest.java",
+ "bytecode/SecureRandomTest.class.data=>bin/classes/test/pkg/SecureRandomTest.class"
+ ));
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/WakelockDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/WakelockDetectorTest.java
new file mode 100644
index 0000000..d0662cb
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/WakelockDetectorTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+@SuppressWarnings("javadoc")
+public class WakelockDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new WakelockDetector();
+ }
+
+ public void test1() throws Exception {
+ assertEquals(
+ "WakelockActivity1.java:15: Warning: Found a wakelock acquire() but no release() calls anywhere",
+
+ lintProject(
+ "bytecode/.classpath=>.classpath",
+ "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
+ "res/layout/onclick.xml=>res/layout/onclick.xml",
+ "bytecode/WakelockActivity1.java.txt=>src/test/pkg/WakelockActivity1.java",
+ "bytecode/WakelockActivity1.class.data=>bin/classes/test/pkg/WakelockActivity1.class"
+ ));
+ }
+
+ public void test2() throws Exception {
+ assertEquals(
+ "WakelockActivity2.java:13: Warning: Wakelocks should be released in onPause, not onDestroy",
+
+ lintProject(
+ "bytecode/.classpath=>.classpath",
+ "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
+ "res/layout/onclick.xml=>res/layout/onclick.xml",
+ "bytecode/WakelockActivity2.java.txt=>src/test/pkg/WakelockActivity2.java",
+ "bytecode/WakelockActivity2.class.data=>bin/classes/test/pkg/WakelockActivity2.class"
+ ));
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data
new file mode 100644
index 0000000..3236187
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt
new file mode 100644
index 0000000..c05fbd9
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt
@@ -0,0 +1,42 @@
+package test.pkg;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+public class SecureRandomTest {
+ private static final long FIXED_SEED = 1000L;
+ protected int getDynamicSeed() { return 1; }
+
+ public void testLiterals() {
+ SecureRandom random1 = new SecureRandom();
+ random1.setSeed(System.currentTimeMillis()); // OK
+ random1.setSeed(getDynamicSeed()); // OK
+ random1.setSeed(0); // Wrong
+ random1.setSeed(1); // Wrong
+ random1.setSeed((int)1023); // Wrong
+ random1.setSeed(1023L); // Wrong
+ random1.setSeed(FIXED_SEED); // Wrong
+ }
+
+ public void testRandomTypeOk() {
+ Random random2 = new Random();
+ random2.setSeed(0); // OK
+ }
+
+ public void testRandomTypeWrong() {
+ Random random3 = new SecureRandom();
+ random3.setSeed(0); // Wrong: owner is java/util/Random, but applied to SecureRandom object
+ }
+
+ public void testBytesOk() {
+ SecureRandom random1 = new SecureRandom();
+ byte[] seed = random1.generateSeed(4);
+ random1.setSeed(seed); // OK
+ }
+
+ public void testBytesWrong() {
+ SecureRandom random2 = new SecureRandom();
+ byte[] seed = new byte[3];
+ random2.setSeed(seed); // Wrong
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data
new file mode 100644
index 0000000..d4733e6
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt
new file mode 100644
index 0000000..0b14691
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt
@@ -0,0 +1,17 @@
+package test.pkg;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.PowerManager;
+
+public class WakelockActivity1 extends Activity {
+ private PowerManager.WakeLock mWakeLock;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ PowerManager manager = (PowerManager) getSystemService(POWER_SERVICE);
+ mWakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Test");
+ mWakeLock.acquire(); // Never released
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data
new file mode 100644
index 0000000..89e35c1
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt
new file mode 100644
index 0000000..fa91a43
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt
@@ -0,0 +1,24 @@
+package test.pkg;
+
+import android.app.Activity;
+import android.os.PowerManager;
+
+public class WakelockActivity2 extends Activity {
+ private PowerManager.WakeLock mWakeLock;
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ mWakeLock.release(); // Should be done in onPause instead
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onDestroy();
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ mWakeLock.release(); // OK
+ }
+ }
+}