Update database clear & job refresh mechanism for time change intent
- Ignore time change intent for time format update - Clear data after current time in DB and refresh periodic job - Take a snapshot of current battery usage stats if no periodic job in DB Bug: 336423923 Bug: 314921894 Fix: 314921894 Test: atest SettingsRoboTests:com.android.settings.fuelgauge.batteryusagei Change-Id: Iec0f5e8e97f18c4603de711a5884336ba0af23a9
This commit is contained in:
@@ -67,7 +67,7 @@ public final class BootBroadcastReceiver extends BroadcastReceiver {
|
|||||||
refreshJobs(context);
|
refreshJobs(context);
|
||||||
break;
|
break;
|
||||||
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 data from action=" + action);
|
||||||
DatabaseUtils.clearDataAfterTimeChangedIfNeeded(context, intent);
|
DatabaseUtils.clearDataAfterTimeChangedIfNeeded(context, intent);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package com.android.settings.fuelgauge.batteryusage;
|
package com.android.settings.fuelgauge.batteryusage;
|
||||||
|
|
||||||
import static android.content.Intent.FLAG_RECEIVER_REPLACE_PENDING;
|
|
||||||
|
|
||||||
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging;
|
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging;
|
||||||
|
|
||||||
import android.app.usage.IUsageStatsManager;
|
import android.app.usage.IUsageStatsManager;
|
||||||
@@ -436,6 +434,23 @@ public final class DatabaseUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Clears data after a specific startTimestamp in the battery usage database. */
|
||||||
|
public static void clearAllAfter(Context context, long startTimestamp) {
|
||||||
|
AsyncTask.execute(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
final BatteryStateDatabase database =
|
||||||
|
BatteryStateDatabase.getInstance(context.getApplicationContext());
|
||||||
|
database.appUsageEventDao().clearAllAfter(startTimestamp);
|
||||||
|
database.batteryEventDao().clearAllAfter(startTimestamp);
|
||||||
|
database.batteryStateDao().clearAllAfter(startTimestamp);
|
||||||
|
database.batteryUsageSlotDao().clearAllAfter(startTimestamp);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Log.e(TAG, "clearAllAfter() failed", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** Clears all out-of-date data in the battery usage database. */
|
/** Clears all out-of-date data in the battery usage database. */
|
||||||
public static void clearExpiredDataIfNeeded(Context context) {
|
public static void clearExpiredDataIfNeeded(Context context) {
|
||||||
AsyncTask.execute(
|
AsyncTask.execute(
|
||||||
@@ -456,14 +471,14 @@ public final class DatabaseUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears all data and jobs if current timestamp is out of the range of last recorded job. */
|
/** Clears data after new updated time and refresh periodic job. */
|
||||||
public static void clearDataAfterTimeChangedIfNeeded(Context context, Intent intent) {
|
public static void clearDataAfterTimeChangedIfNeeded(Context context, Intent intent) {
|
||||||
if ((intent.getFlags() & FLAG_RECEIVER_REPLACE_PENDING) != 0) {
|
if ((intent.hasExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT))) {
|
||||||
BatteryUsageLogUtils.writeLog(
|
BatteryUsageLogUtils.writeLog(
|
||||||
context,
|
context,
|
||||||
Action.TIME_UPDATED,
|
Action.TIME_UPDATED,
|
||||||
"Database is not cleared because the time change intent is only"
|
"Database is not cleared because the time change intent is"
|
||||||
+ " for the existing pending receiver.");
|
+ " for time format change");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AsyncTask.execute(
|
AsyncTask.execute(
|
||||||
@@ -861,36 +876,23 @@ public final class DatabaseUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void clearDataAfterTimeChangedIfNeededInternal(Context context) {
|
private static void clearDataAfterTimeChangedIfNeededInternal(Context context) {
|
||||||
|
final long currentTime = System.currentTimeMillis();
|
||||||
|
final String logInfo =
|
||||||
|
String.format(Locale.ENGLISH, "clear data after current time = %d", currentTime);
|
||||||
|
Log.d(TAG, logInfo);
|
||||||
|
BatteryUsageLogUtils.writeLog(context, Action.TIME_UPDATED, logInfo);
|
||||||
|
DatabaseUtils.clearAllAfter(context, currentTime);
|
||||||
|
PeriodicJobManager.getInstance(context).refreshJob(/* fromBoot= */ false);
|
||||||
|
|
||||||
final List<BatteryEvent> batteryLevelRecordEvents =
|
final List<BatteryEvent> batteryLevelRecordEvents =
|
||||||
DatabaseUtils.getBatteryEvents(
|
DatabaseUtils.getBatteryEvents(
|
||||||
context,
|
context,
|
||||||
Calendar.getInstance(),
|
Calendar.getInstance(),
|
||||||
getLastFullChargeTime(context),
|
getLastFullChargeTime(context),
|
||||||
BATTERY_LEVEL_RECORD_EVENTS);
|
BATTERY_LEVEL_RECORD_EVENTS);
|
||||||
final long lastRecordTimestamp =
|
if (batteryLevelRecordEvents.isEmpty()) {
|
||||||
batteryLevelRecordEvents.isEmpty()
|
// Take a snapshot of battery usage data immediately if there's no battery events.
|
||||||
? INVALID_TIMESTAMP
|
BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true);
|
||||||
: batteryLevelRecordEvents.get(0).getTimestamp();
|
|
||||||
final long nextRecordTimestamp =
|
|
||||||
TimestampUtils.getNextEvenHourTimestamp(lastRecordTimestamp);
|
|
||||||
final long currentTime = System.currentTimeMillis();
|
|
||||||
final boolean isOutOfTimeRange =
|
|
||||||
lastRecordTimestamp == INVALID_TIMESTAMP
|
|
||||||
|| currentTime < lastRecordTimestamp
|
|
||||||
|| currentTime > nextRecordTimestamp;
|
|
||||||
final String logInfo =
|
|
||||||
String.format(
|
|
||||||
Locale.ENGLISH,
|
|
||||||
"clear database = %b, current time = %d, last record time = %d",
|
|
||||||
isOutOfTimeRange,
|
|
||||||
currentTime,
|
|
||||||
lastRecordTimestamp);
|
|
||||||
Log.d(TAG, logInfo);
|
|
||||||
BatteryUsageLogUtils.writeLog(context, Action.TIME_UPDATED, logInfo);
|
|
||||||
if (isOutOfTimeRange) {
|
|
||||||
DatabaseUtils.clearAll(context);
|
|
||||||
PeriodicJobManager.getInstance(context)
|
|
||||||
.refreshJob(/* fromBoot= */ false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -55,6 +55,10 @@ public interface AppUsageEventDao {
|
|||||||
@Query("DELETE FROM AppUsageEventEntity WHERE timestamp <= :timestamp")
|
@Query("DELETE FROM AppUsageEventEntity WHERE timestamp <= :timestamp")
|
||||||
void clearAllBefore(long timestamp);
|
void clearAllBefore(long timestamp);
|
||||||
|
|
||||||
|
/** Deletes all recorded data after a specific timestamp. */
|
||||||
|
@Query("DELETE FROM AppUsageEventEntity WHERE timestamp >= :timestamp")
|
||||||
|
void clearAllAfter(long timestamp);
|
||||||
|
|
||||||
/** Clears all recorded data in the database. */
|
/** Clears all recorded data in the database. */
|
||||||
@Query("DELETE FROM AppUsageEventEntity")
|
@Query("DELETE FROM AppUsageEventEntity")
|
||||||
void clearAll();
|
void clearAll();
|
||||||
|
@@ -65,6 +65,10 @@ public interface BatteryEventDao {
|
|||||||
@Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")
|
@Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")
|
||||||
void clearAllBefore(long timestamp);
|
void clearAllBefore(long timestamp);
|
||||||
|
|
||||||
|
/** Deletes all recorded data after a specific timestamp. */
|
||||||
|
@Query("DELETE FROM BatteryEventEntity WHERE timestamp >= :timestamp")
|
||||||
|
void clearAllAfter(long timestamp);
|
||||||
|
|
||||||
/** Clears all recorded data in the database. */
|
/** Clears all recorded data in the database. */
|
||||||
@Query("DELETE FROM BatteryEventEntity")
|
@Query("DELETE FROM BatteryEventEntity")
|
||||||
void clearAll();
|
void clearAll();
|
||||||
|
@@ -61,6 +61,10 @@ public interface BatteryStateDao {
|
|||||||
@Query("DELETE FROM BatteryState WHERE timestamp <= :timestamp")
|
@Query("DELETE FROM BatteryState WHERE timestamp <= :timestamp")
|
||||||
void clearAllBefore(long timestamp);
|
void clearAllBefore(long timestamp);
|
||||||
|
|
||||||
|
/** Deletes all recorded data after a specific timestamp. */
|
||||||
|
@Query("DELETE FROM BatteryState WHERE timestamp >= :timestamp")
|
||||||
|
void clearAllAfter(long timestamp);
|
||||||
|
|
||||||
/** Clears all recorded data in the database. */
|
/** Clears all recorded data in the database. */
|
||||||
@Query("DELETE FROM BatteryState")
|
@Query("DELETE FROM BatteryState")
|
||||||
void clearAll();
|
void clearAll();
|
||||||
|
@@ -52,6 +52,10 @@ public interface BatteryUsageSlotDao {
|
|||||||
@Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp <= :timestamp")
|
@Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp <= :timestamp")
|
||||||
void clearAllBefore(long timestamp);
|
void clearAllBefore(long timestamp);
|
||||||
|
|
||||||
|
/** Deletes all recorded data after a specific timestamp. */
|
||||||
|
@Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp")
|
||||||
|
void clearAllAfter(long timestamp);
|
||||||
|
|
||||||
/** Clears all recorded data in the database. */
|
/** Clears all recorded data in the database. */
|
||||||
@Query("DELETE FROM BatteryUsageSlotEntity")
|
@Query("DELETE FROM BatteryUsageSlotEntity")
|
||||||
void clearAll();
|
void clearAll();
|
||||||
|
@@ -35,7 +35,6 @@ import com.android.settings.testutils.BatteryTestUtils;
|
|||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
@@ -64,9 +63,8 @@ public final class BootBroadcastReceiverTest {
|
|||||||
|
|
||||||
// Inserts fake data into database for testing.
|
// Inserts fake data into database for testing.
|
||||||
final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
|
final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
|
||||||
BatteryTestUtils.insertDataToBatteryStateTable(
|
|
||||||
mContext, Clock.systemUTC().millis(), "com.android.systemui");
|
|
||||||
mDao = database.batteryStateDao();
|
mDao = database.batteryStateDao();
|
||||||
|
mDao.clearAll();
|
||||||
clearSharedPreferences();
|
clearSharedPreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,10 +127,13 @@ public final class BootBroadcastReceiverTest {
|
|||||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
|
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("b/314921894")
|
|
||||||
@Test
|
@Test
|
||||||
public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob()
|
public void onReceive_withTimeChangedIntentSetEarlierTime_refreshesJob()
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
|
BatteryTestUtils.insertDataToBatteryStateTable(
|
||||||
|
mContext, Clock.systemUTC().millis() + 60000, "com.android.systemui");
|
||||||
|
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
|
||||||
|
|
||||||
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
|
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
|
||||||
|
|
||||||
TimeUnit.MILLISECONDS.sleep(100);
|
TimeUnit.MILLISECONDS.sleep(100);
|
||||||
@@ -140,6 +141,38 @@ public final class BootBroadcastReceiverTest {
|
|||||||
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
|
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onReceive_withTimeChangedIntentSetLaterTime_clearNoDataAndRefreshesJob()
|
||||||
|
throws InterruptedException {
|
||||||
|
BatteryTestUtils.insertDataToBatteryStateTable(
|
||||||
|
mContext, Clock.systemUTC().millis() - 60000, "com.android.systemui");
|
||||||
|
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
|
||||||
|
|
||||||
|
mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
|
||||||
|
|
||||||
|
TimeUnit.MILLISECONDS.sleep(100);
|
||||||
|
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
|
||||||
|
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onReceive_withTimeFormatChangedIntent_skipRefreshJob() throws InterruptedException {
|
||||||
|
BatteryTestUtils.insertDataToBatteryStateTable(
|
||||||
|
mContext, Clock.systemUTC().millis() + 60000, "com.android.systemui");
|
||||||
|
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
|
||||||
|
|
||||||
|
mReceiver.onReceive(
|
||||||
|
mContext,
|
||||||
|
new Intent(Intent.EXTRA_INTENT)
|
||||||
|
.putExtra(
|
||||||
|
Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,
|
||||||
|
Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR));
|
||||||
|
|
||||||
|
TimeUnit.MILLISECONDS.sleep(100);
|
||||||
|
assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
|
||||||
|
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void invokeJobRecheck_broadcastsIntent() {
|
public void invokeJobRecheck_broadcastsIntent() {
|
||||||
BootBroadcastReceiver.invokeJobRecheck(mContext);
|
BootBroadcastReceiver.invokeJobRecheck(mContext);
|
||||||
|
Reference in New Issue
Block a user