Cancel data loading when hourly job happens in the first 40 minutes
after booting in AOSP. Test: make RunSettingsRoboTests + manual Bug: 257384343 Change-Id: I437b68719ff5ece73fa33b74cb144f4262528e8c
This commit is contained in:
@@ -135,6 +135,11 @@ public interface PowerUsageFeatureProvider {
|
||||
*/
|
||||
boolean isExtraDefend();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if delay the hourly job when device is booting.
|
||||
*/
|
||||
boolean delayHourlyJobWhenBooting();
|
||||
|
||||
/**
|
||||
* Gets a intent for one time bypass charge limited to resume charging.
|
||||
*/
|
||||
|
@@ -156,6 +156,11 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delayHourlyJobWhenBooting() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CharSequence> getHideBackgroundUsageTimeSet(Context context) {
|
||||
return new ArraySet<>();
|
||||
|
@@ -24,13 +24,17 @@ import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.core.instrumentation.ElapsedTimeUtils;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/** Receives broadcasts to start or stop the periodic fetching job. */
|
||||
public final class BootBroadcastReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "BootBroadcastReceiver";
|
||||
private static final long RESCHEDULE_FOR_BOOT_ACTION = Duration.ofSeconds(6).toMillis();
|
||||
private static final long RESCHEDULE_FOR_BOOT_ACTION_WITH_DELAY =
|
||||
Duration.ofMinutes(40).toMillis();
|
||||
private static final long RESCHEDULE_FOR_BOOT_ACTION_WITHOUT_DELAY =
|
||||
Duration.ofSeconds(6).toMillis();
|
||||
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
@@ -67,7 +71,7 @@ public final class BootBroadcastReceiver extends BroadcastReceiver {
|
||||
case Intent.ACTION_TIME_CHANGED:
|
||||
Log.d(TAG, "refresh job and clear all data from action=" + action);
|
||||
DatabaseUtils.clearAll(context);
|
||||
PeriodicJobManager.getInstance(context).refreshJob();
|
||||
PeriodicJobManager.getInstance(context).refreshJob(/*fromBoot=*/ false);
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "receive unsupported action=" + action);
|
||||
@@ -78,15 +82,23 @@ public final class BootBroadcastReceiver extends BroadcastReceiver {
|
||||
final Intent recheckIntent = new Intent(ACTION_PERIODIC_JOB_RECHECK);
|
||||
recheckIntent.setClass(context, BootBroadcastReceiver.class);
|
||||
mHandler.postDelayed(() -> context.sendBroadcast(recheckIntent),
|
||||
RESCHEDULE_FOR_BOOT_ACTION);
|
||||
getRescheduleTimeForBootAction(context));
|
||||
} else if (ACTION_SETUP_WIZARD_FINISHED.equals(action)) {
|
||||
ElapsedTimeUtils.storeSuwFinishedTimestamp(context, System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
private long getRescheduleTimeForBootAction(Context context) {
|
||||
final boolean delayHourlyJobWhenBooting =
|
||||
FeatureFactory.getFactory(context)
|
||||
.getPowerUsageFeatureProvider(context)
|
||||
.delayHourlyJobWhenBooting();
|
||||
return delayHourlyJobWhenBooting
|
||||
? RESCHEDULE_FOR_BOOT_ACTION_WITH_DELAY
|
||||
: RESCHEDULE_FOR_BOOT_ACTION_WITHOUT_DELAY;
|
||||
}
|
||||
|
||||
private static void refreshJobs(Context context) {
|
||||
// Clears useless data from battery usage database if needed.
|
||||
DatabaseUtils.clearExpiredDataIfNeeded(context);
|
||||
PeriodicJobManager.getInstance(context).refreshJob();
|
||||
PeriodicJobManager.getInstance(context).refreshJob(/*fromBoot=*/ true);
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,8 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
@@ -45,6 +47,9 @@ public final class PeriodicJobManager {
|
||||
@VisibleForTesting
|
||||
static final int DATA_FETCH_INTERVAL_MINUTE = 60;
|
||||
|
||||
@VisibleForTesting
|
||||
static long sBroadcastDelayFromBoot = Duration.ofMinutes(40).toMillis();
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
void reset() {
|
||||
sSingleton = null; // for testing only
|
||||
@@ -65,7 +70,7 @@ public final class PeriodicJobManager {
|
||||
|
||||
/** Schedules the next alarm job if it is available. */
|
||||
@SuppressWarnings("JavaUtilDate")
|
||||
public void refreshJob() {
|
||||
public void refreshJob(final boolean fromBoot) {
|
||||
if (mAlarmManager == null) {
|
||||
Log.e(TAG, "cannot schedule next alarm job");
|
||||
return;
|
||||
@@ -74,7 +79,7 @@ public final class PeriodicJobManager {
|
||||
final PendingIntent pendingIntent = getPendingIntent();
|
||||
cancelJob(pendingIntent);
|
||||
// Uses UTC time to avoid scheduler is impacted by different timezone.
|
||||
final long triggerAtMillis = getTriggerAtMillis(Clock.systemUTC());
|
||||
final long triggerAtMillis = getTriggerAtMillis(mContext, Clock.systemUTC(), fromBoot);
|
||||
mAlarmManager.setExactAndAllowWhileIdle(
|
||||
AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
|
||||
Log.d(TAG, "schedule next alarm job at "
|
||||
@@ -90,11 +95,21 @@ public final class PeriodicJobManager {
|
||||
}
|
||||
|
||||
/** Gets the next alarm trigger UTC time in milliseconds. */
|
||||
static long getTriggerAtMillis(Clock clock) {
|
||||
static long getTriggerAtMillis(Context context, Clock clock, final boolean fromBoot) {
|
||||
long currentTimeMillis = clock.millis();
|
||||
final boolean delayHourlyJobWhenBooting =
|
||||
FeatureFactory.getFactory(context)
|
||||
.getPowerUsageFeatureProvider(context)
|
||||
.delayHourlyJobWhenBooting();
|
||||
// Rounds to the previous nearest time slot and shifts to the next one.
|
||||
long timeSlotUnit = Duration.ofMinutes(DATA_FETCH_INTERVAL_MINUTE).toMillis();
|
||||
return (currentTimeMillis / timeSlotUnit) * timeSlotUnit + timeSlotUnit;
|
||||
long targetTime = (currentTimeMillis / timeSlotUnit) * timeSlotUnit + timeSlotUnit;
|
||||
if (delayHourlyJobWhenBooting
|
||||
&& fromBoot
|
||||
&& (targetTime - currentTimeMillis) <= sBroadcastDelayFromBoot) {
|
||||
targetTime += timeSlotUnit;
|
||||
}
|
||||
return targetTime;
|
||||
}
|
||||
|
||||
private PendingIntent getPendingIntent() {
|
||||
|
@@ -42,7 +42,7 @@ public final class PeriodicJobReceiver extends BroadcastReceiver {
|
||||
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ false);
|
||||
AppUsageDataLoader.enqueueWork(context);
|
||||
Log.d(TAG, "refresh periodic job from action=" + action);
|
||||
PeriodicJobManager.getInstance(context).refreshJob();
|
||||
PeriodicJobManager.getInstance(context).refreshJob(/*fromBoot=*/ false);
|
||||
DatabaseUtils.clearExpiredDataIfNeeded(context);
|
||||
}
|
||||
}
|
||||
|
@@ -124,28 +124,6 @@ public final class BootBroadcastReceiverTest {
|
||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onReceive_containsExpiredData_clearsExpiredDataFromDatabase()
|
||||
throws InterruptedException {
|
||||
insertExpiredData(/*shiftDay=*/ DatabaseUtils.DATA_RETENTION_INTERVAL_DAY);
|
||||
|
||||
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
assertThat(mDao.getAllAfter(0)).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onReceive_withoutExpiredData_notClearsExpiredDataFromDatabase()
|
||||
throws InterruptedException {
|
||||
insertExpiredData(/*shiftDay=*/ DatabaseUtils.DATA_RETENTION_INTERVAL_DAY - 1);
|
||||
|
||||
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
assertThat(mDao.getAllAfter(0)).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob()
|
||||
throws InterruptedException {
|
||||
|
@@ -57,7 +57,7 @@ public final class PeriodicJobManagerTest {
|
||||
|
||||
@Test
|
||||
public void refreshJob_refreshesAlarmJob() {
|
||||
mPeriodicJobManager.refreshJob();
|
||||
mPeriodicJobManager.refreshJob(/*fromBoot=*/ false);
|
||||
|
||||
final ShadowAlarmManager.ScheduledAlarm alarm =
|
||||
mShadowAlarmManager.peekNextScheduledAlarm();
|
||||
@@ -76,7 +76,7 @@ public final class PeriodicJobManagerTest {
|
||||
FakeClock fakeClock = new FakeClock();
|
||||
fakeClock.setCurrentTime(currentTimeDuration);
|
||||
|
||||
assertThat(PeriodicJobManager.getTriggerAtMillis(fakeClock))
|
||||
assertThat(PeriodicJobManager.getTriggerAtMillis(mContext, fakeClock, /*fromBoot=*/ false))
|
||||
.isEqualTo(currentTimeDuration.plusMinutes(timeSlotUnit).toMillis());
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public final class PeriodicJobManagerTest {
|
||||
fakeClock.setCurrentTime(
|
||||
currentTimeDuration.plusMinutes(1L).plusMillis(51L));
|
||||
|
||||
assertThat(PeriodicJobManager.getTriggerAtMillis(fakeClock))
|
||||
assertThat(PeriodicJobManager.getTriggerAtMillis(mContext, fakeClock, /*fromBoot=*/ true))
|
||||
.isEqualTo(currentTimeDuration.plusMinutes(timeSlotUnit).toMillis());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user