PowerManagerService: Hold a wakelock while mProximityTask is queued

This prevents the device from sleeping while a deferred proximity sensor
event is pending.
Fixes b/2260005 (Proximity sensor can fail due to missing wakelock)

Change-Id: Ia4825f548b5b7d8a0b184f720b32c2f21b77b96e
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index e1425d4..73527b7 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -185,6 +185,7 @@
     private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock;
     private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock;
     private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
+    private UnsynchronizedWakeLock mProximityPartialLock;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     private TimeoutTask mTimeoutTask = new TimeoutTask();
@@ -283,6 +284,7 @@
         IBinder mToken;
         int mCount = 0;
         boolean mRefCounted;
+        boolean mHeld;
 
         UnsynchronizedWakeLock(int flags, String tag, boolean refCounted) {
             mFlags = flags;
@@ -297,6 +299,7 @@
                 try {
                     PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
                             MY_UID, mTag);
+                    mHeld = true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -306,15 +309,21 @@
         public void release() {
             if (!mRefCounted || --mCount == 0) {
                 PowerManagerService.this.releaseWakeLockLocked(mToken, false);
+                mHeld = false;
             }
             if (mCount < 0) {
                 throw new RuntimeException("WakeLock under-locked " + mTag);
             }
         }
 
+        public boolean isHeld()
+        {
+            return mHeld;
+        }
+
         public String toString() {
             return "UnsynchronizedWakeLock(mFlags=0x" + Integer.toHexString(mFlags)
-                    + " mCount=" + mCount + ")";
+                    + " mCount=" + mCount + " mHeld=" + mHeld + ")";
         }
     }
 
@@ -443,6 +452,8 @@
                                 PowerManager.PARTIAL_WAKE_LOCK, "StayOnWhilePluggedIn Partial", false);
         mPreventScreenOnPartialLock = new UnsynchronizedWakeLock(
                                 PowerManager.PARTIAL_WAKE_LOCK, "PreventScreenOn Partial", false);
+        mProximityPartialLock = new UnsynchronizedWakeLock(
+                                PowerManager.PARTIAL_WAKE_LOCK, "Proximity Partial", false);
 
         mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
         mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -907,6 +918,7 @@
         pw.println("  mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock);
         pw.println("  mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock);
         pw.println("  mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock);
+        pw.println("  mProximityPartialLock=" + mProximityPartialLock);
         pw.println("  mProximityWakeLockCount=" + mProximityWakeLockCount);
         pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
         pw.println("  mProximitySensorActive=" + mProximitySensorActive);
@@ -1951,6 +1963,9 @@
                     proximityChangedLocked(mProximityPendingValue == 1);
                     mProximityPendingValue = -1;
                 }
+                if (mProximityPartialLock.isHeld()) {
+                    mProximityPartialLock.release();
+                }
             }
         }
     };
@@ -2412,6 +2427,9 @@
             try {
                 mSensorManager.unregisterListener(mProximityListener);
                 mHandler.removeCallbacks(mProximityTask);
+                if (mProximityPartialLock.isHeld()) {
+                    mProximityPartialLock.release();
+                }
                 mProximitySensorEnabled = false;
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -2478,6 +2496,7 @@
                 long timeSinceLastEvent = milliseconds - mLastProximityEventTime;
                 mLastProximityEventTime = milliseconds;
                 mHandler.removeCallbacks(mProximityTask);
+                boolean proximityTaskQueued = false;
 
                 // compare against getMaximumRange to support sensors that only return 0 or 1
                 boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD &&
@@ -2490,11 +2509,21 @@
                     // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing
                     mProximityPendingValue = (active ? 1 : 0);
                     mHandler.postDelayed(mProximityTask, PROXIMITY_SENSOR_DELAY - timeSinceLastEvent);
+                    proximityTaskQueued = true;
                 } else {
                     // process the value immediately
                     mProximityPendingValue = -1;
                     proximityChangedLocked(active);
                 }
+
+                // update mProximityPartialLock state
+                boolean held = mProximityPartialLock.isHeld();
+                if (!held && proximityTaskQueued) {
+                    // hold wakelock until mProximityTask runs
+                    mProximityPartialLock.acquire();
+                } else if (held && !proximityTaskQueued) {
+                    mProximityPartialLock.release();
+                }
             }
         }