Update QnsTimer Handler on device active mode.

Handler delay is not updated when device moves from idle state to active
state. This cause more delay for the event to expire than expected.

Bug: 279000239
Test: verified with live testing.
Change-Id: I4d47673681a3a764a6fe39611ab6ad9d929818a1
diff --git a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTimer.java b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTimer.java
index f2439bd..7630054 100644
--- a/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTimer.java
+++ b/services/QualifiedNetworksService/src/com/android/telephony/qns/QnsTimer.java
@@ -45,6 +45,11 @@
 
     private static final String TAG = QnsTimer.class.getSimpleName();
     private static final int EVENT_QNS_TIMER_EXPIRED = 1;
+
+    private static final int MIN_ALARM_CALL_ACTIVE_DELAY_MS = 0;
+    private static final int MIN_ALARM_SCREEN_OFF_DELAY_MS = 10000;
+    private static final int MIN_ALARM_DEVICE_LIGHT_IDLE_DELAY_MS = 30000;
+    private static final int MIN_ALARM_DEVICE_IDLE_DELAY_MS = 60000;
     static final String ACTION_ALARM_TIMER_EXPIRED =
             "com.android.telephony.qns.action.ALARM_TIMER_EXPIRED";
 
@@ -56,11 +61,11 @@
     private final BroadcastReceiver mBroadcastReceiver;
     private final PriorityQueue<TimerInfo> mTimerInfos;
     private PendingIntent mPendingIntent;
-    private long mMinAlarmTimeMs = 10000;
+    private long mMinAlarmTimeMs = MIN_ALARM_SCREEN_OFF_DELAY_MS;
     private int mCurrentAlarmTimerId = INVALID_ID;
     private int mCurrentHandlerTimerId = INVALID_ID;
     private boolean mIsAlarmRequired;
-    @VisibleForTesting final Handler mHandler;
+    @VisibleForTesting Handler mHandler;
     private long mLastAlarmTriggerAtMs = Long.MAX_VALUE;
     private int mCallType = CALL_TYPE_IDLE;
 
@@ -100,7 +105,7 @@
         mHandler.post(
                 () -> {
                     mTimerInfos.add(timerInfo);
-                    updateToShortestDelay(mIsAlarmRequired, true);
+                    updateToShortestDelay(mIsAlarmRequired, false /* forceUpdate */);
                 });
         return timerId;
     }
@@ -120,7 +125,7 @@
                     logd("Cancel timerId=" + timerId);
                     TimerInfo timerInfo = new TimerInfo(timerId);
                     if (mTimerInfos.remove(timerInfo) && timerId == mCurrentAlarmTimerId) {
-                        updateToShortestDelay(mIsAlarmRequired, true);
+                        updateToShortestDelay(mIsAlarmRequired, false /* forceUpdate */);
                     }
                 });
     }
@@ -136,20 +141,20 @@
         if (mCallType == CALL_TYPE_IDLE && type != CALL_TYPE_IDLE) {
             mHandler.post(
                     () -> {
-                        mMinAlarmTimeMs = 0;
+                        mMinAlarmTimeMs = MIN_ALARM_CALL_ACTIVE_DELAY_MS;
                         if (mIsAlarmRequired) {
-                            updateToShortestDelay(true, false);
+                            updateToShortestDelay(true, true /* forceUpdate */);
                         }
                     });
         }
         mCallType = type;
         if (mCallType == CALL_TYPE_IDLE && mIsAlarmRequired) {
             if (mPowerManager.isDeviceIdleMode()) {
-                mMinAlarmTimeMs = 60000;
+                mMinAlarmTimeMs = MIN_ALARM_DEVICE_IDLE_DELAY_MS;
             } else if (mPowerManager.isDeviceLightIdleMode()) {
-                mMinAlarmTimeMs = 30000;
+                mMinAlarmTimeMs = MIN_ALARM_DEVICE_LIGHT_IDLE_DELAY_MS;
             } else {
-                mMinAlarmTimeMs = 10000; // SCREEN_OFF case
+                mMinAlarmTimeMs = MIN_ALARM_SCREEN_OFF_DELAY_MS; // SCREEN_OFF case
             }
         }
     }
@@ -161,11 +166,10 @@
      * respective handlers.
      *
      * @param isAlarmRequired flag indicates if timer is need to setup with Alarm.
-     * @param skipTimerUpdate flag indicates if current scheduled alarm timers needs any change.
-     *     This flag will be false when call type changes or device moves or come out of idle state
-     *     because such cases mandates timer update.
+     * @param forceUpdate flag indicates to update the delay time for handler and/or alarm
+     *     forcefully.
      */
-    private void updateToShortestDelay(boolean isAlarmRequired, boolean skipTimerUpdate) {
+    private void updateToShortestDelay(boolean isAlarmRequired, boolean forceUpdate) {
         TimerInfo timerInfo = mTimerInfos.peek();
         long elapsedTime = getSystemElapsedRealTime();
         while (timerInfo != null && timerInfo.getExpireAtElapsedMillis() <= elapsedTime) {
@@ -181,14 +185,14 @@
         }
         long delay = timerInfo.getExpireAtElapsedMillis() - elapsedTime;
         // Delayed Handler will always set for shortest delay.
-        if (timerInfo.getTimerId() != mCurrentHandlerTimerId) {
+        if (timerInfo.getTimerId() != mCurrentHandlerTimerId || forceUpdate) {
             mHandler.removeMessages(EVENT_QNS_TIMER_EXPIRED);
             mHandler.sendEmptyMessageDelayed(EVENT_QNS_TIMER_EXPIRED, delay);
             mCurrentHandlerTimerId = timerInfo.getTimerId();
         }
 
         // Alarm will always set for shortest from Math.max(delay, mMinAlarmTimeMs)
-        if (timerInfo.getTimerId() != mCurrentAlarmTimerId || !skipTimerUpdate) {
+        if (timerInfo.getTimerId() != mCurrentAlarmTimerId || forceUpdate) {
             if (isAlarmRequired) {
                 delay = Math.max(delay, mMinAlarmTimeMs);
                 // check if smaller timer alarm is already running for active timer info.
@@ -245,10 +249,13 @@
                 case Intent.ACTION_SCREEN_OFF:
                     mHandler.post(
                             () -> {
-                                mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 10000 : 0;
+                                mMinAlarmTimeMs =
+                                        (mCallType == CALL_TYPE_IDLE)
+                                                ? MIN_ALARM_SCREEN_OFF_DELAY_MS
+                                                : MIN_ALARM_CALL_ACTIVE_DELAY_MS;
                                 if (!mIsAlarmRequired) {
                                     mIsAlarmRequired = true;
-                                    updateToShortestDelay(true, false);
+                                    updateToShortestDelay(true, true /* forceUpdate */);
                                 }
                             });
                     break;
@@ -257,7 +264,7 @@
                             () -> {
                                 if (mIsAlarmRequired) {
                                     mIsAlarmRequired = false;
-                                    updateToShortestDelay(false, false);
+                                    updateToShortestDelay(false, true /* forceUpdate */);
                                 }
                             });
                     break;
@@ -265,13 +272,19 @@
                     mHandler.post(
                             () -> {
                                 if (mPowerManager.isDeviceLightIdleMode()) {
-                                    mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 30000 : 0;
+                                    mMinAlarmTimeMs =
+                                            (mCallType == CALL_TYPE_IDLE)
+                                                    ? MIN_ALARM_DEVICE_LIGHT_IDLE_DELAY_MS
+                                                    : MIN_ALARM_CALL_ACTIVE_DELAY_MS;
                                     if (!mIsAlarmRequired) {
                                         mIsAlarmRequired = true;
-                                        updateToShortestDelay(true, false);
+                                        updateToShortestDelay(true, true /* forceUpdate */);
                                     }
                                 } else {
-                                    mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 10000 : 0;
+                                    mMinAlarmTimeMs =
+                                            (mCallType == CALL_TYPE_IDLE)
+                                                    ? MIN_ALARM_SCREEN_OFF_DELAY_MS
+                                                    : MIN_ALARM_CALL_ACTIVE_DELAY_MS;
                                 }
                             });
                     break;
@@ -279,13 +292,19 @@
                     mHandler.post(
                             () -> {
                                 if (mPowerManager.isDeviceIdleMode()) {
-                                    mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 60000 : 0;
+                                    mMinAlarmTimeMs =
+                                            (mCallType == CALL_TYPE_IDLE)
+                                                    ? MIN_ALARM_DEVICE_IDLE_DELAY_MS
+                                                    : MIN_ALARM_CALL_ACTIVE_DELAY_MS;
                                     if (!mIsAlarmRequired) {
                                         mIsAlarmRequired = true;
-                                        updateToShortestDelay(true, false);
+                                        updateToShortestDelay(true, true /* forceUpdate */);
                                     }
                                 } else {
-                                    mMinAlarmTimeMs = (mCallType == CALL_TYPE_IDLE) ? 10000 : 0;
+                                    mMinAlarmTimeMs =
+                                            (mCallType == CALL_TYPE_IDLE)
+                                                    ? MIN_ALARM_SCREEN_OFF_DELAY_MS
+                                                    : MIN_ALARM_CALL_ACTIVE_DELAY_MS;
                                 }
                             });
                     break;
@@ -307,7 +326,7 @@
             switch (msg.what) {
                 case EVENT_QNS_TIMER_EXPIRED:
                     logd("Timer expired");
-                    updateToShortestDelay(mIsAlarmRequired, true);
+                    updateToShortestDelay(mIsAlarmRequired, false /* forceUpdate */);
                     break;
                 default:
                     break;
diff --git a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTimerTest.java b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTimerTest.java
index c4acd5a..50a36b5 100644
--- a/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTimerTest.java
+++ b/services/QualifiedNetworksService/tests/src/com/android/telephony/qns/QnsTimerTest.java
@@ -35,6 +35,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -51,6 +52,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.CountDownLatch;
+
 @RunWith(AndroidJUnit4.class)
 public class QnsTimerTest extends QnsTest {
 
@@ -306,4 +309,35 @@
         // assume 100ms as max delay in execution
         assertTrue(delay < setDelay && delay > setDelay - 100);
     }
+
+    @Test
+    public void testDeviceMovesToActiveState() {
+        int setDelay = 30000;
+        CountDownLatch latch = new CountDownLatch(2);
+        HandlerThread ht = new HandlerThread("");
+        ht.start();
+        Handler tempHandler = spy(new Handler(ht.getLooper()));
+        when(mPowerManager.isDeviceLightIdleMode()).thenReturn(true, false);
+        mBroadcastReceiver.onReceive(sMockContext, new Intent(Intent.ACTION_SCREEN_OFF));
+        mBroadcastReceiver.onReceive(
+                sMockContext, new Intent(PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED));
+        mQnsTimer.registerTimer(mMessage, setDelay);
+        waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200);
+        verify(mAlarmManager)
+                .setExactAndAllowWhileIdle(anyInt(), anyLong(), isA(PendingIntent.class));
+        waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 5000);
+
+        mQnsTimer.mHandler = tempHandler;
+        mBroadcastReceiver.onReceive(
+                sMockContext, new Intent(PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED));
+        mBroadcastReceiver.onReceive(sMockContext, new Intent(Intent.ACTION_SCREEN_ON));
+        waitForDelayedHandlerAction(mQnsTimer.mHandler, 10, 200);
+        verify(mAlarmManager).cancel(isA(PendingIntent.class));
+
+        // Handler should reset for the updated delay
+        verify(tempHandler).removeMessages(EVENT_QNS_TIMER_EXPIRED);
+        verify(tempHandler).sendEmptyMessageDelayed(anyInt(), anyLong());
+        assertTrue(mQnsTimer.mHandler.hasMessages(EVENT_QNS_TIMER_EXPIRED));
+        ht.quit();
+    }
 }