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();
|
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.
|
||||||
*/
|
*/
|
||||||
|
@@ -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<>();
|
||||||
|
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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() {
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user