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();
/**
* 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.
*/

View File

@@ -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<>();

View File

@@ -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);
}
}

View File

@@ -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() {

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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());
}
}