diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java index 762ea2458cf..45069224d8a 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java @@ -376,7 +376,7 @@ public class DataProcessManager { // Generates the indexed AppUsagePeriod list data for each corresponding time slot for // further use. mAppUsagePeriodMap = DataProcessor.generateAppUsagePeriodMap( - mHourlyBatteryLevelsPerDay, mAppUsageEventList, mStartTimestampOfLevelData); + mHourlyBatteryLevelsPerDay, mAppUsageEventList); } private void tryToGenerateFinalDataAndApplyCallback() { diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java index 874dc11f320..3b04f907cc6 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java @@ -72,7 +72,6 @@ import java.util.stream.Stream; * usage UI. */ public final class DataProcessor { - private static final boolean DEBUG = false; private static final String TAG = "DataProcessor"; private static final int POWER_COMPONENT_SYSTEM_SERVICES = 7; private static final int POWER_COMPONENT_WAKELOCK = 12; @@ -98,6 +97,9 @@ public final class DataProcessor { @VisibleForTesting static long sFakeCurrentTimeMillis = 0; + @VisibleForTesting + static boolean sDebug = false; + @VisibleForTesting static IUsageStatsManager sUsageStatsManager = IUsageStatsManager.Stub.asInterface( @@ -250,8 +252,7 @@ public final class DataProcessor { public static Map>>>> generateAppUsagePeriodMap( final List hourlyBatteryLevelsPerDay, - final List appUsageEventList, - final long startTimestampOfLevelData) { + final List appUsageEventList) { if (appUsageEventList.isEmpty()) { Log.w(TAG, "appUsageEventList is empty"); return null; @@ -261,12 +262,8 @@ public final class DataProcessor { Collections.sort(appUsageEventList, TIMESTAMP_COMPARATOR); final Map>>>> resultMap = new HashMap<>(); - // Starts from the first index within expected time range. The events before the time range - // will be bypassed directly. - int currentAppUsageEventIndex = - getFirstUsageEventIndex(appUsageEventList, startTimestampOfLevelData); - final int appUsageEventSize = appUsageEventList.size(); + final long dailySize = hourlyBatteryLevelsPerDay.size(); for (int dailyIndex = 0; dailyIndex < hourlyBatteryLevelsPerDay.size(); dailyIndex++) { final Map>>> dailyMap = new HashMap<>(); @@ -275,32 +272,21 @@ public final class DataProcessor { continue; } final List timestamps = hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps(); + final long hourlySize = timestamps.size() - 1; for (int hourlyIndex = 0; hourlyIndex < timestamps.size() - 1; hourlyIndex++) { // The start and end timestamps of this slot should be the adjacent timestamps. final long startTimestamp = timestamps.get(hourlyIndex); - final long endTimestamp = timestamps.get(hourlyIndex + 1); + // The final slot is to show the data from last even hour until now but the + // timestamp in hourlyBatteryLevelsPerDay is not the real value. So use current + // timestamp instead of reading the timestamp from hourlyBatteryLevelsPerDay here. + final long endTimestamp = + dailyIndex == dailySize - 1 && hourlyIndex == hourlySize - 1 && !sDebug + ? System.currentTimeMillis() : timestamps.get(hourlyIndex + 1); // Gets the app usage event list for this hourly slot first. - final List hourlyAppUsageEventList = new ArrayList<>(); - while (currentAppUsageEventIndex < appUsageEventSize) { - // If current event is null, go for next directly. - if (appUsageEventList.get(currentAppUsageEventIndex) == null) { - currentAppUsageEventIndex++; - continue; - } - final long timestamp = - appUsageEventList.get(currentAppUsageEventIndex).getTimestamp(); - // If the timestamp is already later than the end time, stop the loop. - if (timestamp >= endTimestamp) { - break; - } - // If timestamp is within the time range, add it into the list. - if (timestamp >= startTimestamp) { - hourlyAppUsageEventList.add( - appUsageEventList.get(currentAppUsageEventIndex)); - } - currentAppUsageEventIndex++; - } + final List hourlyAppUsageEventList = + getAppUsageEventListWithinTimeRangeWithBuffer( + appUsageEventList, startTimestamp, endTimestamp); // The value could be null when there is no data in the hourly slot. dailyMap.put( @@ -785,13 +771,11 @@ public final class DataProcessor { final List usageEvents, final long startTime, final long endTime) { final List usagePeriodList = new ArrayList<>(); AppUsagePeriod.Builder pendingUsagePeriod = AppUsagePeriod.newBuilder(); - boolean hasMetStartEvent = false; for (final AppUsageEvent event : usageEvents) { final long eventTime = event.getTimestamp(); if (event.getType() == AppUsageEventType.ACTIVITY_RESUMED) { - hasMetStartEvent = true; // If there is an existing start time, simply ignore this start event. // If there was no start time, then start a new period. if (!pendingUsagePeriod.hasStartTime()) { @@ -800,37 +784,31 @@ public final class DataProcessor { } else if (event.getType() == AppUsageEventType.ACTIVITY_STOPPED) { pendingUsagePeriod.setEndTime(eventTime); if (!pendingUsagePeriod.hasStartTime()) { - // If we haven't met start event in this list, the start event might happen - // in the previous time slot. use the startTime for this period. - // Otherwise, add one for the default duration. - if (!hasMetStartEvent) { - hasMetStartEvent = true; - pendingUsagePeriod.setStartTime(startTime); - } else { - pendingUsagePeriod.setStartTime( - getStartTimeForIncompleteUsagePeriod(pendingUsagePeriod)); - } + pendingUsagePeriod.setStartTime( + getStartTimeForIncompleteUsagePeriod(pendingUsagePeriod)); } // If we already have start time, add it directly. - addToPeriodList(usagePeriodList, pendingUsagePeriod.build()); + validateAndAddToPeriodList( + usagePeriodList, pendingUsagePeriod.build(), startTime, endTime); pendingUsagePeriod.clear(); } else if (event.getType() == AppUsageEventType.DEVICE_SHUTDOWN) { - hasMetStartEvent = true; // The end event might be lost when device is shutdown. Use the estimated end // time for the period. if (pendingUsagePeriod.hasStartTime()) { pendingUsagePeriod.setEndTime( getEndTimeForIncompleteUsagePeriod(pendingUsagePeriod, eventTime)); - addToPeriodList(usagePeriodList, pendingUsagePeriod.build()); + validateAndAddToPeriodList( + usagePeriodList, pendingUsagePeriod.build(), startTime, endTime); pendingUsagePeriod.clear(); } } } // If there exists unclosed period, the stop event might happen in the next time // slot. Use the endTime for the period. - if (pendingUsagePeriod.hasStartTime()) { + if (pendingUsagePeriod.hasStartTime() && pendingUsagePeriod.getStartTime() < endTime) { pendingUsagePeriod.setEndTime(endTime); - addToPeriodList(usagePeriodList, pendingUsagePeriod.build()); + validateAndAddToPeriodList( + usagePeriodList, pendingUsagePeriod.build(), startTime, endTime); pendingUsagePeriod.clear(); } return usagePeriodList; @@ -881,12 +859,56 @@ public final class DataProcessor { } } - private static void addToPeriodList( - final List appUsagePeriodList, final AppUsagePeriod appUsagePeriod) { - // Only when the period is valid, add it into the list. - if (appUsagePeriod.getStartTime() < appUsagePeriod.getEndTime()) { - appUsagePeriodList.add(appUsagePeriod); + /** + * Generates the list of {@link AppUsageEvent} within the specific time range. + * The buffer is added to make sure the app usage calculation near the boundaries is correct. + * + * Note: The appUsageEventList should have been sorted when calling this function. + */ + private static List getAppUsageEventListWithinTimeRangeWithBuffer( + final List appUsageEventList, final long startTime, final long endTime) { + final long start = startTime - DatabaseUtils.USAGE_QUERY_BUFFER_HOURS; + final long end = endTime + DatabaseUtils.USAGE_QUERY_BUFFER_HOURS; + final List resultList = new ArrayList<>(); + for (final AppUsageEvent event : appUsageEventList) { + final long eventTime = event.getTimestamp(); + // Because the appUsageEventList has been sorted, if any event is already after the end + // time, all the following events should be able to drop directly. + if (eventTime > end) { + break; + } + // If the event timestamp is in [start, end], add it into the result list. + if (eventTime >= start) { + resultList.add(event); + } } + return resultList; + } + + private static void validateAndAddToPeriodList( + final List appUsagePeriodList, + final AppUsagePeriod appUsagePeriod, + final long startTime, + final long endTime) { + final long periodStartTime = + trimPeriodTime(appUsagePeriod.getStartTime(), startTime, endTime); + final long periodEndTime = trimPeriodTime(appUsagePeriod.getEndTime(), startTime, endTime); + // Only when the period is valid, add it into the list. + if (periodStartTime < periodEndTime) { + final AppUsagePeriod period = + AppUsagePeriod.newBuilder() + .setStartTime(periodStartTime) + .setEndTime(periodEndTime) + .build(); + appUsagePeriodList.add(period); + } + } + + private static long trimPeriodTime( + final long originalTime, final long startTime, final long endTime) { + long finalTime = Math.max(originalTime, startTime); + finalTime = Math.min(finalTime, endTime); + return finalTime; } private static void addToUsagePeriodMap( @@ -918,19 +940,6 @@ public final class DataProcessor { eventTime); } - private static int getFirstUsageEventIndex( - final List appUsageEventList, - final long startTimestampOfLevelData) { - int currentIndex = 0; - while (currentIndex < appUsageEventList.size() - && (appUsageEventList.get(currentIndex) == null - || appUsageEventList.get(currentIndex).getTimestamp() - < startTimestampOfLevelData)) { - currentIndex++; - } - return currentIndex; - } - private static void insertHourlyDeviceScreenOnTime( final Map>>>> appUsagePeriodMap, @@ -1520,7 +1529,7 @@ public final class DataProcessor { foregroundUsageTimeInMs + backgroundUsageTimeInMs; if (totalUsageTimeInMs > TOTAL_HOURLY_TIME_THRESHOLD) { final float ratio = TOTAL_HOURLY_TIME_THRESHOLD / totalUsageTimeInMs; - if (DEBUG) { + if (sDebug) { Log.w(TAG, String.format("abnormal usage time %d|%d for:\n%s", Duration.ofMillis(foregroundUsageTimeInMs).getSeconds(), Duration.ofMillis(backgroundUsageTimeInMs).getSeconds(), @@ -1876,7 +1885,7 @@ public final class DataProcessor { private static void log(Context context, final String content, final long timestamp, final BatteryHistEntry entry) { - if (DEBUG) { + if (sDebug) { Log.d(TAG, String.format(entry != null ? "%s %s:\n%s" : "%s %s:%s", utcToLocalTime(context, timestamp), content, entry)); } diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java index 516645ed75b..122768694cc 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java @@ -74,6 +74,11 @@ public final class DatabaseUtils { public static final String QUERY_KEY_USERID = "userid"; public static final long INVALID_USER_ID = Integer.MIN_VALUE; + /** + * The buffer hours to query app usage events that may have begun or ended out of the final + * desired time frame. + */ + public static final long USAGE_QUERY_BUFFER_HOURS = Duration.ofHours(3).toMillis(); /** A content URI to access battery usage states data. */ public static final Uri BATTERY_CONTENT_URI = @@ -138,7 +143,10 @@ public final class DatabaseUtils { final long startTimestampOfLevelData) { final long startTime = System.currentTimeMillis(); final long sixDaysAgoTimestamp = getTimestampSixDaysAgo(calendar); - final long queryTimestamp = Math.max(startTimestampOfLevelData, sixDaysAgoTimestamp); + // Query a longer time period and then trim to the original time period in order to make + // sure the app usage calculation near the boundaries is correct. + final long queryTimestamp = + Math.max(startTimestampOfLevelData, sixDaysAgoTimestamp) - USAGE_QUERY_BUFFER_HOURS; Log.d(TAG, "sixDayAgoTimestamp: " + sixDaysAgoTimestamp); final String queryUserIdString = userIds.stream() .map(userId -> String.valueOf(userId)) 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 9eaf1b66c27..576184de6d6 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java @@ -178,22 +178,22 @@ public final class DataProcessorTest { @Test public void generateAppUsagePeriodMap_returnExpectedResult() { + DataProcessor.sDebug = true; final List hourlyBatteryLevelsPerDay = new ArrayList<>(); final String packageName = "com.android.settings"; // Adds the day 1 data. - final List timestamps1 = List.of(10000L, 20000L, 30000L); + final List timestamps1 = List.of(14400000L, 18000000L, 21600000L); final List levels1 = List.of(100, 100, 100); hourlyBatteryLevelsPerDay.add( new BatteryLevelData.PeriodBatteryLevelData(timestamps1, levels1)); // Adds the day 2 data. hourlyBatteryLevelsPerDay.add(null); // Adds the day 3 data. - final List timestamps2 = List.of(40000L, 50000L); + final List timestamps2 = List.of(45200000L, 48800000L); final List levels2 = List.of(100, 100); hourlyBatteryLevelsPerDay.add( new BatteryLevelData.PeriodBatteryLevelData(timestamps2, levels2)); - final long startTimestampOfLevelData = 10000L; final List appUsageEventList = new ArrayList<>(); // Adds some events before the start timestamp. appUsageEventList.add(buildAppUsageEvent( @@ -204,39 +204,48 @@ public final class DataProcessorTest { /*instanceId=*/ 2, packageName)); // Adds the valid app usage events. appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 10000L, /*userId=*/ 1, + AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 4200000L, /*userId=*/ 1, /*instanceId=*/ 2, packageName)); appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 15000L, /*userId=*/ 1, + AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 4500000L, /*userId=*/ 1, /*instanceId=*/ 2, packageName)); appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 12000L, /*userId=*/ 2, + AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 12600000L, /*userId=*/ 2, /*instanceId=*/ 3, packageName)); appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 18000L, /*userId=*/ 2, + AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 15600000L, /*userId=*/ 2, /*instanceId=*/ 3, packageName)); appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 35000L, /*userId=*/ 1, + AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 16200000L, /*userId=*/ 2, + /*instanceId=*/ 3, packageName)); + appUsageEventList.add(buildAppUsageEvent( + AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 18000000L, /*userId=*/ 2, + /*instanceId=*/ 3, packageName)); + appUsageEventList.add(buildAppUsageEvent( + AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 17200000L, /*userId=*/ 1, /*instanceId=*/ 2, packageName)); appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 45000L, /*userId=*/ 1, + AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 17800000L, /*userId=*/ 1, /*instanceId=*/ 2, packageName)); appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 42000L, /*userId=*/ 1, + AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 46000000L, /*userId=*/ 1, + /*instanceId=*/ 2, packageName)); + appUsageEventList.add(buildAppUsageEvent( + AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 47800000L, /*userId=*/ 1, + /*instanceId=*/ 2, packageName)); + appUsageEventList.add(buildAppUsageEvent( + AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 49000000L, /*userId=*/ 1, + /*instanceId=*/ 2, packageName)); + appUsageEventList.add(buildAppUsageEvent( + AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 59600000L, /*userId=*/ 1, /*instanceId=*/ 4, packageName)); appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 52000L, /*userId=*/ 1, - /*instanceId=*/ 4, packageName)); - appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 55000L, /*userId=*/ 1, - /*instanceId=*/ 4, packageName)); - appUsageEventList.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 58000L, /*userId=*/ 1, + AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 61200000L, /*userId=*/ 1, /*instanceId=*/ 4, packageName)); final Map>>>> periodMap = DataProcessor.generateAppUsagePeriodMap( - hourlyBatteryLevelsPerDay, appUsageEventList, startTimestampOfLevelData); + hourlyBatteryLevelsPerDay, appUsageEventList); assertThat(periodMap.size()).isEqualTo(3); // Day 1 @@ -246,11 +255,12 @@ public final class DataProcessorTest { Map> userMap = hourlyMap.get(1L); assertThat(userMap.size()).isEqualTo(1); assertThat(userMap.get(packageName).size()).isEqualTo(1); - assertAppUsagePeriod(userMap.get(packageName).get(0), 10000, 15000); + assertAppUsagePeriod(userMap.get(packageName).get(0), 17200000L, 17800000L); userMap = hourlyMap.get(2L); assertThat(userMap.size()).isEqualTo(1); - assertThat(userMap.get(packageName).size()).isEqualTo(1); - assertAppUsagePeriod(userMap.get(packageName).get(0), 12000, 18000); + assertThat(userMap.get(packageName).size()).isEqualTo(2); + assertAppUsagePeriod(userMap.get(packageName).get(0), 14400000L, 15600000L); + assertAppUsagePeriod(userMap.get(packageName).get(1), 16200000L, 18000000L); hourlyMap = periodMap.get(0).get(1); assertThat(hourlyMap).isNull(); // Day 2 @@ -262,8 +272,8 @@ public final class DataProcessorTest { userMap = hourlyMap.get(1L); assertThat(userMap.size()).isEqualTo(1); assertThat(userMap.get(packageName).size()).isEqualTo(2); - assertAppUsagePeriod(userMap.get(packageName).get(0), 40000, 45000); - assertAppUsagePeriod(userMap.get(packageName).get(1), 42000, 50000); + assertAppUsagePeriod(userMap.get(packageName).get(0), 45970000L, 46000000L); + assertAppUsagePeriod(userMap.get(packageName).get(1), 47800000L, 48800000L); } @Test @@ -273,7 +283,7 @@ public final class DataProcessorTest { hourlyBatteryLevelsPerDay.add( new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>())); assertThat(DataProcessor.generateAppUsagePeriodMap( - hourlyBatteryLevelsPerDay, new ArrayList<>(), 0)).isNull(); + hourlyBatteryLevelsPerDay, new ArrayList<>())).isNull(); } @Test @@ -1508,9 +1518,19 @@ public final class DataProcessorTest { @Test public void buildAppUsagePeriodListPerInstance_returnExpectedResult() { final List appUsageEvents = new ArrayList<>(); + // Fake data earlier than time range. + appUsageEvents.add(buildAppUsageEvent( + AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1)); + appUsageEvents.add(buildAppUsageEvent( + AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 2)); + // Fake resume event earlier than time range. + appUsageEvents.add(buildAppUsageEvent( + AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 3)); + appUsageEvents.add(buildAppUsageEvent( + AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 120000)); // Fake normal data. appUsageEvents.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 100000)); + AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 150000)); appUsageEvents.add(buildAppUsageEvent( AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 200000)); // Fake two adjacent resume events. @@ -1540,29 +1560,16 @@ public final class DataProcessorTest { AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1000000)); final List appUsagePeriodList = - DataProcessor.buildAppUsagePeriodListPerInstance(appUsageEvents, 0, 1100000); + DataProcessor.buildAppUsagePeriodListPerInstance(appUsageEvents, 100000, 1100000); - assertThat(appUsagePeriodList.size()).isEqualTo(6); - assertAppUsagePeriod(appUsagePeriodList.get(0), 100000, 200000); - assertAppUsagePeriod(appUsagePeriodList.get(1), 300000, 500000); - assertAppUsagePeriod(appUsagePeriodList.get(2), 570000, 600000); - assertAppUsagePeriod(appUsagePeriodList.get(3), 700000, 730000); - assertAppUsagePeriod(appUsagePeriodList.get(4), 900000, 910000); - assertAppUsagePeriod(appUsagePeriodList.get(5), 1000000, 1100000); - } - - @Test - public void buildAppUsagePeriodListPerInstance_notMetStart_returnExpectedResult() { - final List appUsageEvents = new ArrayList<>(); - // Start with stop event. - appUsageEvents.add(buildAppUsageEvent( - AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 100000)); - - final List appUsagePeriodList = - DataProcessor.buildAppUsagePeriodListPerInstance(appUsageEvents, 0, 200000); - - assertThat(appUsageEvents.size()).isEqualTo(1); - assertAppUsagePeriod(appUsagePeriodList.get(0), 0, 100000); + assertThat(appUsagePeriodList.size()).isEqualTo(7); + assertAppUsagePeriod(appUsagePeriodList.get(0), 100000, 120000); + assertAppUsagePeriod(appUsagePeriodList.get(1), 150000, 200000); + assertAppUsagePeriod(appUsagePeriodList.get(2), 300000, 500000); + assertAppUsagePeriod(appUsagePeriodList.get(3), 570000, 600000); + assertAppUsagePeriod(appUsagePeriodList.get(4), 700000, 730000); + assertAppUsagePeriod(appUsagePeriodList.get(5), 900000, 910000); + assertAppUsagePeriod(appUsagePeriodList.get(6), 1000000, 1100000); } @Test