diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ad815516c61..e085be86407 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3343,6 +3343,7 @@ + diff --git a/protos/fuelgauge_log.proto b/protos/fuelgauge_log.proto index 4bee75ca058..b16958d8e2b 100644 --- a/protos/fuelgauge_log.proto +++ b/protos/fuelgauge_log.proto @@ -45,6 +45,7 @@ message BatteryUsageHistoricalLogEntry { FETCH_USAGE_DATA = 4; INSERT_USAGE_DATA = 5; TIME_UPDATED = 6; + TIMEZONE_UPDATED = 7; } optional int64 timestamp = 1; diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index b938c72e4b2..5e17f4b4871 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -649,9 +649,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private final class HourlyChartLabelTextGenerator extends BaseLabelTextGenerator implements BatteryChartViewModel.LabelTextGenerator { - private static final int FULL_CHARGE_BATTERY_LEVEL = 100; - - private boolean mIsFromFullCharge; + private boolean mIsStartTimestamp; private long mFistTimestamp; private long mLatestTimestamp; @@ -664,7 +662,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll long timestamp = timestamps.get(index); boolean showMinute = false; if (Objects.equal(timestamp, mFistTimestamp)) { - if (mIsFromFullCharge) { + if (mIsStartTimestamp) { showMinute = true; } else { // starts from 7 days ago @@ -699,8 +697,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll @NonNull final BatteryLevelData batteryLevelData) { BatteryLevelData.PeriodBatteryLevelData firstDayLevelData = batteryLevelData.getHourlyBatteryLevelsPerDay().get(0); - this.mIsFromFullCharge = - firstDayLevelData.getLevels().get(0) == FULL_CHARGE_BATTERY_LEVEL; + this.mIsStartTimestamp = firstDayLevelData.isStartTimestamp(); this.mFistTimestamp = firstDayLevelData.getTimestamps().get(0); this.mLatestTimestamp = getLast( diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java index 231c730a176..d1bf49b6cc5 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java @@ -28,6 +28,7 @@ import androidx.annotation.VisibleForTesting; import androidx.core.util.Preconditions; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -39,17 +40,24 @@ public final class BatteryLevelData { private static final long MIN_SIZE = 2; private static final long TIME_SLOT = DateUtils.HOUR_IN_MILLIS * 2; + // For testing only. + @VisibleForTesting @Nullable static Calendar sTestCalendar; + /** A container for the battery timestamp and level data. */ public static final class PeriodBatteryLevelData { // The length of mTimestamps and mLevels must be the same. mLevels[index] might be null when // there is no level data for the corresponding timestamp. private final List mTimestamps; private final List mLevels; + private final boolean mIsStartTimestamp; public PeriodBatteryLevelData( - @NonNull Map batteryLevelMap, @NonNull List timestamps) { + @NonNull Map batteryLevelMap, + @NonNull List timestamps, + boolean isStartTimestamp) { mTimestamps = timestamps; mLevels = new ArrayList<>(timestamps.size()); + mIsStartTimestamp = isStartTimestamp; for (Long timestamp : timestamps) { mLevels.add( batteryLevelMap.containsKey(timestamp) @@ -66,6 +74,10 @@ public final class BatteryLevelData { return mLevels; } + public boolean isStartTimestamp() { + return mIsStartTimestamp; + } + @Override public String toString() { return String.format( @@ -105,14 +117,21 @@ public final class BatteryLevelData { final List timestampList = new ArrayList<>(batteryLevelMap.keySet()); Collections.sort(timestampList); + final long minTimestamp = timestampList.get(0); + final long sixDaysAgoTimestamp = + DatabaseUtils.getTimestampSixDaysAgo(sTestCalendar != null ? sTestCalendar : null); + final boolean isStartTimestamp = minTimestamp > sixDaysAgoTimestamp; final List dailyTimestamps = getDailyTimestamps(timestampList); final List> hourlyTimestamps = getHourlyTimestamps(dailyTimestamps); - mDailyBatteryLevels = new PeriodBatteryLevelData(batteryLevelMap, dailyTimestamps); + mDailyBatteryLevels = + new PeriodBatteryLevelData(batteryLevelMap, dailyTimestamps, isStartTimestamp); mHourlyBatteryLevelsPerDay = new ArrayList<>(hourlyTimestamps.size()); - for (List hourlyTimestampsPerDay : hourlyTimestamps) { + for (int i = 0; i < hourlyTimestamps.size(); i++) { + final List hourlyTimestampsPerDay = hourlyTimestamps.get(i); mHourlyBatteryLevelsPerDay.add( - new PeriodBatteryLevelData(batteryLevelMap, hourlyTimestampsPerDay)); + new PeriodBatteryLevelData( + batteryLevelMap, hourlyTimestampsPerDay, isStartTimestamp && i == 0)); } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java index e407c636ddf..b758df4bd6b 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java @@ -67,9 +67,13 @@ public final class BootBroadcastReceiver extends BroadcastReceiver { refreshJobs(context); break; 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); break; + case Intent.ACTION_TIMEZONE_CHANGED: + Log.d(TAG, "refresh job and clear all data from action=" + action); + DatabaseUtils.clearDataAfterTimeZoneChangedIfNeeded(context); + break; default: Log.w(TAG, "receive unsupported action=" + action); } diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java index a41e9bd0388..5b28abb422f 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java @@ -16,8 +16,6 @@ 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 android.app.usage.IUsageStatsManager; @@ -59,6 +57,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.TimeZone; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -436,6 +435,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. */ public static void clearExpiredDataIfNeeded(Context context) { AsyncTask.execute( @@ -456,14 +472,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) { - if ((intent.getFlags() & FLAG_RECEIVER_REPLACE_PENDING) != 0) { + if ((intent.hasExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT))) { BatteryUsageLogUtils.writeLog( context, Action.TIME_UPDATED, - "Database is not cleared because the time change intent is only" - + " for the existing pending receiver."); + "Database is not cleared because the time change intent is" + + " for time format change"); return; } AsyncTask.execute( @@ -480,6 +496,22 @@ public final class DatabaseUtils { }); } + /** Clears all data and reset jobs if timezone changed. */ + public static void clearDataAfterTimeZoneChangedIfNeeded(Context context) { + AsyncTask.execute( + () -> { + try { + clearDataAfterTimeZoneChangedIfNeededInternal(context); + } catch (RuntimeException e) { + Log.e(TAG, "clearDataAfterTimeZoneChangedIfNeeded() failed", e); + BatteryUsageLogUtils.writeLog( + context, + Action.TIMEZONE_UPDATED, + "clearDataAfterTimeZoneChangedIfNeeded() failed" + e); + } + }); + } + /** Returns the timestamp for 00:00 6 days before the calendar date. */ public static long getTimestampSixDaysAgo(Calendar calendar) { Calendar startCalendar = @@ -861,37 +893,38 @@ public final class DatabaseUtils { } 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 batteryLevelRecordEvents = DatabaseUtils.getBatteryEvents( context, Calendar.getInstance(), getLastFullChargeTime(context), BATTERY_LEVEL_RECORD_EVENTS); - final long lastRecordTimestamp = - batteryLevelRecordEvents.isEmpty() - ? INVALID_TIMESTAMP - : 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; + if (batteryLevelRecordEvents.isEmpty()) { + // Take a snapshot of battery usage data immediately if there's no battery events. + BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true); + } + } + + private static void clearDataAfterTimeZoneChangedIfNeededInternal(Context context) { final String logInfo = String.format( Locale.ENGLISH, - "clear database = %b, current time = %d, last record time = %d", - isOutOfTimeRange, - currentTime, - lastRecordTimestamp); + "clear database for new time zone = %s", + TimeZone.getDefault().toString()); + BatteryUsageLogUtils.writeLog(context, Action.TIMEZONE_UPDATED, logInfo); Log.d(TAG, logInfo); - BatteryUsageLogUtils.writeLog(context, Action.TIME_UPDATED, logInfo); - if (isOutOfTimeRange) { - DatabaseUtils.clearAll(context); - PeriodicJobManager.getInstance(context) - .refreshJob(/* fromBoot= */ false); - } + DatabaseUtils.clearAll(context); + PeriodicJobManager.getInstance(context).refreshJob(/* fromBoot= */ false); + // Take a snapshot of battery usage data immediately + BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true); } private static long loadLongFromContentProvider( diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java index d220b15968f..249780125f2 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java +++ b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java @@ -55,6 +55,10 @@ public interface AppUsageEventDao { @Query("DELETE FROM AppUsageEventEntity WHERE timestamp <= :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. */ @Query("DELETE FROM AppUsageEventEntity") void clearAll(); diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java index 8b696fe96c0..19d20438afa 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java +++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java @@ -65,6 +65,10 @@ public interface BatteryEventDao { @Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :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. */ @Query("DELETE FROM BatteryEventEntity") void clearAll(); diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java index 520c6bed484..049251eb718 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java +++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java @@ -61,6 +61,10 @@ public interface BatteryStateDao { @Query("DELETE FROM BatteryState WHERE timestamp <= :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. */ @Query("DELETE FROM BatteryState") void clearAll(); diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java index d8cf41d0ce8..d53b0cf2532 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java +++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java @@ -52,6 +52,10 @@ public interface BatteryUsageSlotDao { @Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp <= :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. */ @Query("DELETE FROM BatteryUsageSlotEntity") void clearAll(); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java index f62fdb8ce6f..44a16f19cca 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java @@ -50,6 +50,7 @@ import android.widget.TextView; import com.android.settings.SettingsActivity; import com.android.settings.testutils.FakeFeatureFactory; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -58,6 +59,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.Calendar; import java.util.List; import java.util.Locale; import java.util.Map; @@ -84,10 +86,13 @@ public final class BatteryChartPreferenceControllerTest { MockitoAnnotations.initMocks(this); Locale.setDefault(new Locale("en_US")); org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false); - TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + final TimeZone timeZone = TimeZone.getTimeZone("UTC"); + TimeZone.setDefault(timeZone); DataProcessor.sTestSystemAppsPackageNames = Set.of(); mFeatureFactory = FakeFeatureFactory.setupForTest(); mContext = spy(RuntimeEnvironment.application); + BatteryLevelData.sTestCalendar = Calendar.getInstance(); + BatteryLevelData.sTestCalendar.setTimeZone(timeZone); doReturn(mContext).when(mContext).getApplicationContext(); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); doReturn(true).when(mUserManager).isUserUnlocked(anyInt()); @@ -115,6 +120,11 @@ public final class BatteryChartPreferenceControllerTest { new BatteryEntry.NameAndIcon("fakeName", /* icon= */ null, /* iconId= */ 1)); } + @After + public void tearDown() { + BatteryLevelData.sTestCalendar = null; + } + @Test public void onDestroy_activityIsChanging_clearBatteryEntryCache() { doReturn(true).when(mSettingsActivity).isChangingConfigurations(); @@ -141,7 +151,8 @@ public final class BatteryChartPreferenceControllerTest { reset(mHourlyChartView); setupHourlyChartViewAnimationMock(); - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0)); verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE); // Ignore fast refresh ui from the data processor callback. @@ -178,7 +189,8 @@ public final class BatteryChartPreferenceControllerTest { BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS, mBatteryChartPreferenceController.mDailyChartLabelTextGenerator); - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0)); verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE); verify(mViewPropertyAnimator, atLeastOnce()).alpha(0f); @@ -283,7 +295,8 @@ public final class BatteryChartPreferenceControllerTest { public void onBatteryLevelDataUpdate_oneDay_showHourlyChartOnly() { doReturn(View.GONE).when(mHourlyChartView).getVisibility(); - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0)); verify(mChartSummaryTextView).setVisibility(View.VISIBLE); verify(mDailyChartView).setVisibility(View.GONE); @@ -295,7 +308,8 @@ public final class BatteryChartPreferenceControllerTest { doReturn(View.GONE).when(mHourlyChartView).getVisibility(); mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL; - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0)); verify(mChartSummaryTextView).setVisibility(View.VISIBLE); verify(mDailyChartView).setVisibility(View.VISIBLE); @@ -307,7 +321,8 @@ public final class BatteryChartPreferenceControllerTest { doReturn(View.GONE).when(mHourlyChartView).getVisibility(); mBatteryChartPreferenceController.mDailyChartIndex = 0; - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0)); verify(mChartSummaryTextView).setVisibility(View.VISIBLE); verify(mDailyChartView).setVisibility(View.VISIBLE); @@ -379,7 +394,8 @@ public final class BatteryChartPreferenceControllerTest { @Test public void selectedSlotText_selectAllDaysAllHours_returnNull() { - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0)); mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL; mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL; @@ -390,7 +406,8 @@ public final class BatteryChartPreferenceControllerTest { @Test public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() { - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0)); mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL; @@ -401,7 +418,8 @@ public final class BatteryChartPreferenceControllerTest { @Test public void selectedSlotText_selectADayAllHours_onlyDayText() { - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0)); mBatteryChartPreferenceController.mDailyChartIndex = 1; mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL; @@ -412,7 +430,8 @@ public final class BatteryChartPreferenceControllerTest { @Test public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() { - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0)); mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mHourlyChartIndex = 2; @@ -426,7 +445,8 @@ public final class BatteryChartPreferenceControllerTest { @Test public void selectedSlotText_SelectADayAnHour_dayAndHourText() { - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0)); mBatteryChartPreferenceController.mDailyChartIndex = 1; mBatteryChartPreferenceController.mHourlyChartIndex = 8; @@ -439,8 +459,9 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void selectedSlotText_selectFirstSlot_withMinuteText() { - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); + public void selectedSlotText_selectFirstSlotAfterFullCharged_withMinuteText() { + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0)); mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mHourlyChartIndex = 0; @@ -452,9 +473,29 @@ public final class BatteryChartPreferenceControllerTest { .isEqualTo("Battery level percentage from 100% to 99%"); } + @Test + public void selectedSlotText_selectFirstSlotAfterTimeUpdated_withMinuteText() { + BatteryLevelData batteryLevelData = + createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 10); + assertThat(batteryLevelData.getHourlyBatteryLevelsPerDay().get(0).isStartTimestamp()) + .isTrue(); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 10)); + mBatteryChartPreferenceController.mDailyChartIndex = 0; + mBatteryChartPreferenceController.mHourlyChartIndex = 0; + + assertThat(mBatteryChartPreferenceController.getSlotInformation(false)) + .isEqualTo("7:01 AM - 8 AM"); + assertThat(mBatteryChartPreferenceController.getSlotInformation(true)) + .isEqualTo("7:01 AM to 8 AM"); + assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo()) + .isEqualTo("Battery level percentage from 90% to 89%"); + } + @Test public void selectedSlotText_selectLastSlot_withNowText() { - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0)); mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mHourlyChartIndex = 3; @@ -468,7 +509,8 @@ public final class BatteryChartPreferenceControllerTest { @Test public void selectedSlotText_selectOnlySlot_withMinuteAndNowText() { - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(1)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 1, /* levelOffset= */ 0)); mBatteryChartPreferenceController.mDailyChartIndex = 0; mBatteryChartPreferenceController.mHourlyChartIndex = 0; @@ -493,7 +535,8 @@ public final class BatteryChartPreferenceControllerTest { mBatteryChartPreferenceController.mHourlyChartIndex = -1; mBatteryChartPreferenceController.onCreate(bundle); - mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(25)); + mBatteryChartPreferenceController.onBatteryLevelDataUpdate( + createBatteryLevelData(/* numOfHours= */ 25, /* levelOffset= */ 0)); assertThat(mBatteryChartPreferenceController.mDailyChartIndex) .isEqualTo(expectedDailyIndex); @@ -503,7 +546,8 @@ public final class BatteryChartPreferenceControllerTest { @Test public void getTotalHours_getExpectedResult() { - BatteryLevelData batteryLevelData = createBatteryLevelData(60); + BatteryLevelData batteryLevelData = + createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0); final int totalHour = BatteryChartPreferenceController.getTotalHours(batteryLevelData); @@ -516,10 +560,10 @@ public final class BatteryChartPreferenceControllerTest { return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS; } - private static BatteryLevelData createBatteryLevelData(int numOfHours) { + private static BatteryLevelData createBatteryLevelData(int numOfHours, int levelOffset) { Map batteryLevelMap = new ArrayMap<>(); for (int index = 0; index < numOfHours; index += 2) { - final Integer level = 100 - index; + final Integer level = 100 - index - levelOffset; Long timestamp = generateTimestamp(index); if (index == 0) { timestamp += DateUtils.MINUTE_IN_MILLIS; @@ -529,6 +573,8 @@ public final class BatteryChartPreferenceControllerTest { } long current = generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS * 2; batteryLevelMap.put(current, 66); + + BatteryLevelData.sTestCalendar.setTimeInMillis(current); DataProcessor.sTestCurrentTimeMillis = current; return new BatteryLevelData(batteryLevelMap); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java index df330a36ac5..545f7733f41 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java @@ -35,7 +35,6 @@ import com.android.settings.testutils.BatteryTestUtils; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -64,9 +63,8 @@ public final class BootBroadcastReceiverTest { // Inserts fake data into database for testing. final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext); - BatteryTestUtils.insertDataToBatteryStateTable( - mContext, Clock.systemUTC().millis(), "com.android.systemui"); mDao = database.batteryStateDao(); + mDao.clearAll(); clearSharedPreferences(); } @@ -129,10 +127,13 @@ public final class BootBroadcastReceiverTest { assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull(); } - @Ignore("b/314921894") @Test - public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob() + public void onReceive_withTimeChangedIntentSetEarlierTime_refreshesJob() 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); @@ -140,6 +141,52 @@ public final class BootBroadcastReceiverTest { 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 + public void onReceive_withTimeZoneChangedIntent_clearAllDataAndRefreshesJob() + throws InterruptedException { + BatteryTestUtils.insertDataToBatteryStateTable( + mContext, Clock.systemUTC().millis(), "com.android.systemui"); + assertThat(mDao.getAllAfter(0).size()).isEqualTo(1); + + mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIMEZONE_CHANGED)); + + TimeUnit.MILLISECONDS.sleep(100); + assertThat(mDao.getAllAfter(0)).isEmpty(); + assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull(); + } + @Test public void invokeJobRecheck_broadcastsIntent() { BootBroadcastReceiver.invokeJobRecheck(mContext); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java index 7faca0d0960..60428014048 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java @@ -170,7 +170,8 @@ public final class DataProcessManagerTest { final Map batteryLevelMap1 = Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100); hourlyBatteryLevelsPerDay.add( - new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap1, timestamps1)); + new BatteryLevelData.PeriodBatteryLevelData( + batteryLevelMap1, timestamps1, /* isStartTimestamp= */ false)); // Adds the day 2 data. hourlyBatteryLevelsPerDay.add(null); // Adds the day 3 data. @@ -178,7 +179,8 @@ public final class DataProcessManagerTest { final Map batteryLevelMap2 = Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100); hourlyBatteryLevelsPerDay.add( - new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap2, timestamps2)); + new BatteryLevelData.PeriodBatteryLevelData( + batteryLevelMap2, timestamps2, /* isStartTimestamp= */ false)); // Fake current usage data. final UsageEvents.Event event1 = getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /* timestamp= */ 1, packageName); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java index 28973430ce9..ae4c56d035a 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java @@ -209,7 +209,8 @@ public final class DataProcessorTest { final Map batteryLevelMap1 = Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100); hourlyBatteryLevelsPerDay.add( - new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap1, timestamps1)); + new BatteryLevelData.PeriodBatteryLevelData( + batteryLevelMap1, timestamps1, /* isStartTimestamp= */ false)); // Adds the day 2 data. hourlyBatteryLevelsPerDay.add(null); // Adds the day 3 data. @@ -217,7 +218,8 @@ public final class DataProcessorTest { final Map batteryLevelMap2 = Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100); hourlyBatteryLevelsPerDay.add( - new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap2, timestamps2)); + new BatteryLevelData.PeriodBatteryLevelData( + batteryLevelMap2, timestamps2, /* isStartTimestamp= */ false)); final List appUsageEventList = new ArrayList<>(); // Adds some events before the start timestamp. appUsageEventList.add( @@ -365,7 +367,8 @@ public final class DataProcessorTest { final List hourlyBatteryLevelsPerDay = new ArrayList<>(); hourlyBatteryLevelsPerDay.add( - new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>())); + new BatteryLevelData.PeriodBatteryLevelData( + new ArrayMap<>(), new ArrayList<>(), /* isStartTimestamp= */ false)); assertThat( DataProcessor.generateAppUsagePeriodMap( mContext, @@ -858,7 +861,8 @@ public final class DataProcessorTest { new ArrayList<>(); hourlyBatteryLevelsPerDay.add( - new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>())); + new BatteryLevelData.PeriodBatteryLevelData( + new ArrayMap<>(), new ArrayList<>(), /* isStartTimestamp= */ false)); assertThat( DataProcessor.getBatteryDiffDataMap(