Delay idle alarm if there's an upcoming AlarmClock alarm.
The system won't go into deep idle when there's an AlarmClock set to go off in the next hour. Knowing this, it doesn't make a lot of sense to schedule an alarm to see if we can go into idle before the AlarmClock alarm goes off. Changing to schedule the idle alarm to go off after the AlarmClock alarm. Bug: 135975445 Test: atest com.android.server.DeviceIdleControllerTest Change-Id: I98bf4131b7b4a1006e37aa42bcbc7dda7e137d1d
This commit is contained in:
@@ -1742,6 +1742,12 @@ public class DeviceIdleController extends SystemService
|
|||||||
return mConstants;
|
return mConstants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns the current elapsed realtime in milliseconds. */
|
||||||
|
long getElapsedRealtime() {
|
||||||
|
return SystemClock.elapsedRealtime();
|
||||||
|
}
|
||||||
|
|
||||||
LocationManager getLocationManager() {
|
LocationManager getLocationManager() {
|
||||||
if (mLocationManager == null) {
|
if (mLocationManager == null) {
|
||||||
mLocationManager = mContext.getSystemService(LocationManager.class);
|
mLocationManager = mContext.getSystemService(LocationManager.class);
|
||||||
@@ -2023,7 +2029,7 @@ public class DeviceIdleController extends SystemService
|
|||||||
|
|
||||||
private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) {
|
private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
// Artifically force the constraint to inactive to unblock anything waiting for it.
|
// Artificially force the constraint to inactive to unblock anything waiting for it.
|
||||||
onConstraintStateChangedLocked(constraint, /* active= */ false);
|
onConstraintStateChangedLocked(constraint, /* active= */ false);
|
||||||
|
|
||||||
// Let the constraint know that we are not listening to it any more.
|
// Let the constraint know that we are not listening to it any more.
|
||||||
@@ -2746,9 +2752,18 @@ public class DeviceIdleController extends SystemService
|
|||||||
mState = STATE_QUICK_DOZE_DELAY;
|
mState = STATE_QUICK_DOZE_DELAY;
|
||||||
// Make sure any motion sensing or locating is stopped.
|
// Make sure any motion sensing or locating is stopped.
|
||||||
resetIdleManagementLocked();
|
resetIdleManagementLocked();
|
||||||
// Wait a small amount of time in case something (eg: background service from
|
if (isUpcomingAlarmClock()) {
|
||||||
// recently closed app) needs to finish running.
|
// If there's an upcoming AlarmClock alarm, we won't go into idle, so
|
||||||
scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
|
// setting a wakeup alarm before the upcoming alarm is futile. Set the quick
|
||||||
|
// doze alarm to after the upcoming AlarmClock alarm.
|
||||||
|
scheduleAlarmLocked(
|
||||||
|
mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
|
||||||
|
+ mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
|
||||||
|
} else {
|
||||||
|
// Wait a small amount of time in case something (eg: background service from
|
||||||
|
// recently closed app) needs to finish running.
|
||||||
|
scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
|
||||||
|
}
|
||||||
EventLogTags.writeDeviceIdle(mState, "no activity");
|
EventLogTags.writeDeviceIdle(mState, "no activity");
|
||||||
} else if (mState == STATE_ACTIVE) {
|
} else if (mState == STATE_ACTIVE) {
|
||||||
mState = STATE_INACTIVE;
|
mState = STATE_INACTIVE;
|
||||||
@@ -2758,7 +2773,16 @@ public class DeviceIdleController extends SystemService
|
|||||||
if (shouldUseIdleTimeoutFactorLocked()) {
|
if (shouldUseIdleTimeoutFactorLocked()) {
|
||||||
delay = (long) (mPreIdleFactor * delay);
|
delay = (long) (mPreIdleFactor * delay);
|
||||||
}
|
}
|
||||||
scheduleAlarmLocked(delay, false);
|
if (isUpcomingAlarmClock()) {
|
||||||
|
// If there's an upcoming AlarmClock alarm, we won't go into idle, so
|
||||||
|
// setting a wakeup alarm before the upcoming alarm is futile. Set the idle
|
||||||
|
// alarm to after the upcoming AlarmClock alarm.
|
||||||
|
scheduleAlarmLocked(
|
||||||
|
mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
|
||||||
|
+ delay, false);
|
||||||
|
} else {
|
||||||
|
scheduleAlarmLocked(delay, false);
|
||||||
|
}
|
||||||
EventLogTags.writeDeviceIdle(mState, "no activity");
|
EventLogTags.writeDeviceIdle(mState, "no activity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2906,13 +2930,21 @@ public class DeviceIdleController extends SystemService
|
|||||||
return mState;
|
return mState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there's an upcoming AlarmClock alarm that is soon enough to prevent the
|
||||||
|
* device from going into idle.
|
||||||
|
*/
|
||||||
|
private boolean isUpcomingAlarmClock() {
|
||||||
|
return mInjector.getElapsedRealtime() + mConstants.MIN_TIME_TO_ALARM
|
||||||
|
>= mAlarmManager.getNextWakeFromIdleTime();
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void stepIdleStateLocked(String reason) {
|
void stepIdleStateLocked(String reason) {
|
||||||
if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
|
if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
|
||||||
EventLogTags.writeDeviceIdleStep();
|
EventLogTags.writeDeviceIdleStep();
|
||||||
|
|
||||||
final long now = SystemClock.elapsedRealtime();
|
if (isUpcomingAlarmClock()) {
|
||||||
if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
|
|
||||||
// Whoops, there is an upcoming alarm. We don't actually want to go idle.
|
// Whoops, there is an upcoming alarm. We don't actually want to go idle.
|
||||||
if (mState != STATE_ACTIVE) {
|
if (mState != STATE_ACTIVE) {
|
||||||
mActiveReason = ACTIVE_REASON_ALARM;
|
mActiveReason = ACTIVE_REASON_ALARM;
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
|
|||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.ArgumentMatchers.longThat;
|
import static org.mockito.ArgumentMatchers.longThat;
|
||||||
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.reset;
|
import static org.mockito.Mockito.reset;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -129,9 +130,12 @@ public class DeviceIdleControllerTest {
|
|||||||
ConnectivityService connectivityService;
|
ConnectivityService connectivityService;
|
||||||
LocationManager locationManager;
|
LocationManager locationManager;
|
||||||
ConstraintController constraintController;
|
ConstraintController constraintController;
|
||||||
|
// Freeze time for testing.
|
||||||
|
long nowElapsed;
|
||||||
|
|
||||||
InjectorForTest(Context ctx) {
|
InjectorForTest(Context ctx) {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
|
nowElapsed = SystemClock.elapsedRealtime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -155,6 +159,11 @@ public class DeviceIdleControllerTest {
|
|||||||
return connectivityService;
|
return connectivityService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
long getElapsedRealtime() {
|
||||||
|
return nowElapsed;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
LocationManager getLocationManager() {
|
LocationManager getLocationManager() {
|
||||||
return locationManager;
|
return locationManager;
|
||||||
@@ -494,11 +503,44 @@ public class DeviceIdleControllerTest {
|
|||||||
mDeviceIdleController.becomeActiveLocked("testing", 0);
|
mDeviceIdleController.becomeActiveLocked("testing", 0);
|
||||||
verifyStateConditions(STATE_ACTIVE);
|
verifyStateConditions(STATE_ACTIVE);
|
||||||
|
|
||||||
|
setAlarmSoon(false);
|
||||||
setChargingOn(false);
|
setChargingOn(false);
|
||||||
setScreenOn(false);
|
setScreenOn(false);
|
||||||
|
|
||||||
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
|
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
|
||||||
verifyStateConditions(STATE_INACTIVE);
|
verifyStateConditions(STATE_INACTIVE);
|
||||||
|
verify(mDeviceIdleController)
|
||||||
|
.scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStateActiveToStateInactive_UpcomingAlarm() {
|
||||||
|
final long timeUntilAlarm = mConstants.MIN_TIME_TO_ALARM / 2;
|
||||||
|
// Set an upcoming alarm that will prevent full idle.
|
||||||
|
doReturn(mInjector.getElapsedRealtime() + timeUntilAlarm)
|
||||||
|
.when(mAlarmManager).getNextWakeFromIdleTime();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(mDeviceIdleController);
|
||||||
|
|
||||||
|
enterDeepState(STATE_ACTIVE);
|
||||||
|
setQuickDozeEnabled(false);
|
||||||
|
setChargingOn(false);
|
||||||
|
setScreenOn(false);
|
||||||
|
|
||||||
|
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
|
||||||
|
verifyStateConditions(STATE_INACTIVE);
|
||||||
|
inOrder.verify(mDeviceIdleController)
|
||||||
|
.scheduleAlarmLocked(eq(timeUntilAlarm + mConstants.INACTIVE_TIMEOUT), eq(false));
|
||||||
|
|
||||||
|
enterDeepState(STATE_ACTIVE);
|
||||||
|
setQuickDozeEnabled(true);
|
||||||
|
setChargingOn(false);
|
||||||
|
setScreenOn(false);
|
||||||
|
|
||||||
|
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
|
||||||
|
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
||||||
|
inOrder.verify(mDeviceIdleController).scheduleAlarmLocked(
|
||||||
|
eq(timeUntilAlarm + mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -515,42 +557,68 @@ public class DeviceIdleControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransitionFromAnyStateToStateQuickDozeDelay() {
|
public void testTransitionFromAnyStateToStateQuickDozeDelay() {
|
||||||
|
setAlarmSoon(false);
|
||||||
|
InOrder inOrder = inOrder(mDeviceIdleController);
|
||||||
|
|
||||||
enterDeepState(STATE_ACTIVE);
|
enterDeepState(STATE_ACTIVE);
|
||||||
setQuickDozeEnabled(true);
|
setQuickDozeEnabled(true);
|
||||||
setChargingOn(false);
|
setChargingOn(false);
|
||||||
setScreenOn(false);
|
setScreenOn(false);
|
||||||
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
||||||
|
inOrder.verify(mDeviceIdleController)
|
||||||
|
.scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
|
||||||
|
|
||||||
enterDeepState(STATE_INACTIVE);
|
enterDeepState(STATE_INACTIVE);
|
||||||
setQuickDozeEnabled(true);
|
setQuickDozeEnabled(true);
|
||||||
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
||||||
|
inOrder.verify(mDeviceIdleController)
|
||||||
|
.scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
|
||||||
|
|
||||||
enterDeepState(STATE_IDLE_PENDING);
|
enterDeepState(STATE_IDLE_PENDING);
|
||||||
setQuickDozeEnabled(true);
|
setQuickDozeEnabled(true);
|
||||||
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
||||||
|
inOrder.verify(mDeviceIdleController)
|
||||||
|
.scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
|
||||||
|
|
||||||
enterDeepState(STATE_SENSING);
|
enterDeepState(STATE_SENSING);
|
||||||
setQuickDozeEnabled(true);
|
setQuickDozeEnabled(true);
|
||||||
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
||||||
|
inOrder.verify(mDeviceIdleController)
|
||||||
|
.scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
|
||||||
|
|
||||||
enterDeepState(STATE_LOCATING);
|
enterDeepState(STATE_LOCATING);
|
||||||
setQuickDozeEnabled(true);
|
setQuickDozeEnabled(true);
|
||||||
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
||||||
|
inOrder.verify(mDeviceIdleController)
|
||||||
|
.scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
|
||||||
|
|
||||||
// IDLE should stay as IDLE.
|
// IDLE should stay as IDLE.
|
||||||
enterDeepState(STATE_IDLE);
|
enterDeepState(STATE_IDLE);
|
||||||
|
// Clear out any alarm setting from the order before checking for this section.
|
||||||
|
inOrder.verify(mDeviceIdleController, atLeastOnce())
|
||||||
|
.scheduleAlarmLocked(anyLong(), anyBoolean());
|
||||||
setQuickDozeEnabled(true);
|
setQuickDozeEnabled(true);
|
||||||
verifyStateConditions(STATE_IDLE);
|
verifyStateConditions(STATE_IDLE);
|
||||||
|
inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean());
|
||||||
|
|
||||||
// IDLE_MAINTENANCE should stay as IDLE_MAINTENANCE.
|
// IDLE_MAINTENANCE should stay as IDLE_MAINTENANCE.
|
||||||
enterDeepState(STATE_IDLE_MAINTENANCE);
|
enterDeepState(STATE_IDLE_MAINTENANCE);
|
||||||
|
// Clear out any alarm setting from the order before checking for this section.
|
||||||
|
inOrder.verify(mDeviceIdleController, atLeastOnce())
|
||||||
|
.scheduleAlarmLocked(anyLong(), anyBoolean());
|
||||||
setQuickDozeEnabled(true);
|
setQuickDozeEnabled(true);
|
||||||
verifyStateConditions(STATE_IDLE_MAINTENANCE);
|
verifyStateConditions(STATE_IDLE_MAINTENANCE);
|
||||||
|
inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean());
|
||||||
|
|
||||||
|
// State is already QUICK_DOZE_DELAY. No work should be done.
|
||||||
enterDeepState(STATE_QUICK_DOZE_DELAY);
|
enterDeepState(STATE_QUICK_DOZE_DELAY);
|
||||||
|
// Clear out any alarm setting from the order before checking for this section.
|
||||||
|
inOrder.verify(mDeviceIdleController, atLeastOnce())
|
||||||
|
.scheduleAlarmLocked(anyLong(), anyBoolean());
|
||||||
setQuickDozeEnabled(true);
|
setQuickDozeEnabled(true);
|
||||||
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
|
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
|
||||||
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
|
||||||
|
inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user