OSDN Git Service

Allow timed wakelocks to be released gracefully.
authorJeff Sharkey <jsharkey@android.com>
Wed, 5 Jul 2017 18:34:04 +0000 (12:34 -0600)
committerJeff Sharkey <jsharkey@android.com>
Wed, 5 Jul 2017 22:58:51 +0000 (16:58 -0600)
When using a reference-counted wakelock together with a timeout, we
can end up with an over-released wakelock which causes an app crash.

To mitigate this, only throw based on count of external
acquire/release events.

Bug: 62326550
Test: cts-tradefed run commandAndExit cts-dev -m CtsOsTestCases -t android.os.cts.PowerManager_WakeLockTest
Change-Id: I66480cb9798f9404ae3d45a70d68eae136d289de

core/java/android/os/PowerManager.java

index 1c676b3..3f5b611 100644 (file)
@@ -266,7 +266,13 @@ public final class PowerManager {
      * {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} wake lock until the proximity sensor
      * indicates that an object is not in close proximity.
      */
-    public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1;
+    public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1 << 0;
+
+    /**
+     * Flag for {@link WakeLock#release(int)} when called due to timeout.
+     * @hide
+     */
+    public static final int RELEASE_FLAG_TIMEOUT = 1 << 16;
 
     /**
      * Brightness value for fully on.
@@ -1242,7 +1248,8 @@ public final class PowerManager {
         private String mTag;
         private final String mPackageName;
         private final IBinder mToken;
-        private int mCount;
+        private int mInternalCount;
+        private int mExternalCount;
         private boolean mRefCounted = true;
         private boolean mHeld;
         private WorkSource mWorkSource;
@@ -1251,7 +1258,7 @@ public final class PowerManager {
 
         private final Runnable mReleaser = new Runnable() {
             public void run() {
-                release();
+                release(RELEASE_FLAG_TIMEOUT);
             }
         };
 
@@ -1328,7 +1335,9 @@ public final class PowerManager {
         }
 
         private void acquireLocked() {
-            if (!mRefCounted || mCount++ == 0) {
+            mInternalCount++;
+            mExternalCount++;
+            if (!mRefCounted || mInternalCount == 1) {
                 // Do this even if the wake lock is already thought to be held (mHeld == true)
                 // because non-reference counted wake locks are not always properly released.
                 // For example, the keyguard's wake lock might be forcibly released by the
@@ -1373,7 +1382,11 @@ public final class PowerManager {
          */
         public void release(int flags) {
             synchronized (mToken) {
-                if (!mRefCounted || --mCount == 0) {
+                mInternalCount--;
+                if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
+                    mExternalCount--;
+                }
+                if (!mRefCounted || mInternalCount == 0) {
                     mHandler.removeCallbacks(mReleaser);
                     if (mHeld) {
                         Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
@@ -1385,7 +1398,7 @@ public final class PowerManager {
                         mHeld = false;
                     }
                 }
-                if (mCount < 0) {
+                if (mRefCounted && mExternalCount < 0) {
                     throw new RuntimeException("WakeLock under-locked " + mTag);
                 }
             }
@@ -1469,7 +1482,7 @@ public final class PowerManager {
             synchronized (mToken) {
                 return "WakeLock{"
                     + Integer.toHexString(System.identityHashCode(this))
-                    + " held=" + mHeld + ", refCount=" + mCount + "}";
+                    + " held=" + mHeld + ", refCount=" + mInternalCount + "}";
             }
         }