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:
Kuan Wang
2022-12-08 10:41:12 +08:00
parent 269b14fb45
commit cf7e1141df
7 changed files with 51 additions and 36 deletions

View File

@@ -135,6 +135,11 @@ public interface PowerUsageFeatureProvider {
*/ */
boolean isExtraDefend(); 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. * Gets a intent for one time bypass charge limited to resume charging.
*/ */

View File

@@ -156,6 +156,11 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
return false; return false;
} }
@Override
public boolean delayHourlyJobWhenBooting() {
return true;
}
@Override @Override
public Set<CharSequence> getHideBackgroundUsageTimeSet(Context context) { public Set<CharSequence> getHideBackgroundUsageTimeSet(Context context) {
return new ArraySet<>(); return new ArraySet<>();

View File

@@ -24,13 +24,17 @@ import android.os.Looper;
import android.util.Log; import android.util.Log;
import com.android.settings.core.instrumentation.ElapsedTimeUtils; import com.android.settings.core.instrumentation.ElapsedTimeUtils;
import com.android.settings.overlay.FeatureFactory;
import java.time.Duration; import java.time.Duration;
/** Receives broadcasts to start or stop the periodic fetching job. */ /** Receives broadcasts to start or stop the periodic fetching job. */
public final class BootBroadcastReceiver extends BroadcastReceiver { public final class BootBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "BootBroadcastReceiver"; 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()); private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -67,7 +71,7 @@ public final class BootBroadcastReceiver extends BroadcastReceiver {
case Intent.ACTION_TIME_CHANGED: case Intent.ACTION_TIME_CHANGED:
Log.d(TAG, "refresh job and clear all data from action=" + action); Log.d(TAG, "refresh job and clear all data from action=" + action);
DatabaseUtils.clearAll(context); DatabaseUtils.clearAll(context);
PeriodicJobManager.getInstance(context).refreshJob(); PeriodicJobManager.getInstance(context).refreshJob(/*fromBoot=*/ false);
break; break;
default: default:
Log.w(TAG, "receive unsupported action=" + action); 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); final Intent recheckIntent = new Intent(ACTION_PERIODIC_JOB_RECHECK);
recheckIntent.setClass(context, BootBroadcastReceiver.class); recheckIntent.setClass(context, BootBroadcastReceiver.class);
mHandler.postDelayed(() -> context.sendBroadcast(recheckIntent), mHandler.postDelayed(() -> context.sendBroadcast(recheckIntent),
RESCHEDULE_FOR_BOOT_ACTION); getRescheduleTimeForBootAction(context));
} else if (ACTION_SETUP_WIZARD_FINISHED.equals(action)) { } else if (ACTION_SETUP_WIZARD_FINISHED.equals(action)) {
ElapsedTimeUtils.storeSuwFinishedTimestamp(context, System.currentTimeMillis()); 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) { private static void refreshJobs(Context context) {
// Clears useless data from battery usage database if needed. PeriodicJobManager.getInstance(context).refreshJob(/*fromBoot=*/ true);
DatabaseUtils.clearExpiredDataIfNeeded(context);
PeriodicJobManager.getInstance(context).refreshJob();
} }
} }

View File

@@ -24,6 +24,8 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settings.overlay.FeatureFactory;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Clock; import java.time.Clock;
import java.time.Duration; import java.time.Duration;
@@ -45,6 +47,9 @@ public final class PeriodicJobManager {
@VisibleForTesting @VisibleForTesting
static final int DATA_FETCH_INTERVAL_MINUTE = 60; static final int DATA_FETCH_INTERVAL_MINUTE = 60;
@VisibleForTesting
static long sBroadcastDelayFromBoot = Duration.ofMinutes(40).toMillis();
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
void reset() { void reset() {
sSingleton = null; // for testing only sSingleton = null; // for testing only
@@ -65,7 +70,7 @@ public final class PeriodicJobManager {
/** Schedules the next alarm job if it is available. */ /** Schedules the next alarm job if it is available. */
@SuppressWarnings("JavaUtilDate") @SuppressWarnings("JavaUtilDate")
public void refreshJob() { public void refreshJob(final boolean fromBoot) {
if (mAlarmManager == null) { if (mAlarmManager == null) {
Log.e(TAG, "cannot schedule next alarm job"); Log.e(TAG, "cannot schedule next alarm job");
return; return;
@@ -74,7 +79,7 @@ public final class PeriodicJobManager {
final PendingIntent pendingIntent = getPendingIntent(); final PendingIntent pendingIntent = getPendingIntent();
cancelJob(pendingIntent); cancelJob(pendingIntent);
// Uses UTC time to avoid scheduler is impacted by different timezone. // 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( mAlarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent); AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
Log.d(TAG, "schedule next alarm job at " 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. */ /** 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(); 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. // Rounds to the previous nearest time slot and shifts to the next one.
long timeSlotUnit = Duration.ofMinutes(DATA_FETCH_INTERVAL_MINUTE).toMillis(); 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() { private PendingIntent getPendingIntent() {

View File

@@ -42,7 +42,7 @@ public final class PeriodicJobReceiver extends BroadcastReceiver {
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ false); BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ false);
AppUsageDataLoader.enqueueWork(context); AppUsageDataLoader.enqueueWork(context);
Log.d(TAG, "refresh periodic job from action=" + action); Log.d(TAG, "refresh periodic job from action=" + action);
PeriodicJobManager.getInstance(context).refreshJob(); PeriodicJobManager.getInstance(context).refreshJob(/*fromBoot=*/ false);
DatabaseUtils.clearExpiredDataIfNeeded(context); DatabaseUtils.clearExpiredDataIfNeeded(context);
} }
} }

View File

@@ -124,28 +124,6 @@ public final class BootBroadcastReceiverTest {
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull(); 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 @Test
public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob() public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob()
throws InterruptedException { throws InterruptedException {

View File

@@ -57,7 +57,7 @@ public final class PeriodicJobManagerTest {
@Test @Test
public void refreshJob_refreshesAlarmJob() { public void refreshJob_refreshesAlarmJob() {
mPeriodicJobManager.refreshJob(); mPeriodicJobManager.refreshJob(/*fromBoot=*/ false);
final ShadowAlarmManager.ScheduledAlarm alarm = final ShadowAlarmManager.ScheduledAlarm alarm =
mShadowAlarmManager.peekNextScheduledAlarm(); mShadowAlarmManager.peekNextScheduledAlarm();
@@ -76,7 +76,7 @@ public final class PeriodicJobManagerTest {
FakeClock fakeClock = new FakeClock(); FakeClock fakeClock = new FakeClock();
fakeClock.setCurrentTime(currentTimeDuration); fakeClock.setCurrentTime(currentTimeDuration);
assertThat(PeriodicJobManager.getTriggerAtMillis(fakeClock)) assertThat(PeriodicJobManager.getTriggerAtMillis(mContext, fakeClock, /*fromBoot=*/ false))
.isEqualTo(currentTimeDuration.plusMinutes(timeSlotUnit).toMillis()); .isEqualTo(currentTimeDuration.plusMinutes(timeSlotUnit).toMillis());
} }
@@ -89,7 +89,7 @@ public final class PeriodicJobManagerTest {
fakeClock.setCurrentTime( fakeClock.setCurrentTime(
currentTimeDuration.plusMinutes(1L).plusMillis(51L)); currentTimeDuration.plusMinutes(1L).plusMillis(51L));
assertThat(PeriodicJobManager.getTriggerAtMillis(fakeClock)) assertThat(PeriodicJobManager.getTriggerAtMillis(mContext, fakeClock, /*fromBoot=*/ true))
.isEqualTo(currentTimeDuration.plusMinutes(timeSlotUnit).toMillis()); .isEqualTo(currentTimeDuration.plusMinutes(timeSlotUnit).toMillis());
} }
} }