Generate intermediate app usage data after all usage data has been
loaded for further use. Test: make RunSettingsRoboTests + manually Bug: 260964903 Change-Id: I52d96151cab112adca68573f3b47a53b7152f2c0
This commit is contained in:
@@ -33,7 +33,6 @@ import com.android.settings.Utils;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -92,6 +91,13 @@ public class DataProcessManager {
|
|||||||
private boolean mShowBatteryLevel = true;
|
private boolean mShowBatteryLevel = true;
|
||||||
|
|
||||||
private List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
|
private List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
|
||||||
|
/**
|
||||||
|
* The indexed {@link AppUsagePeriod} list data for each corresponding time slot.
|
||||||
|
* <p>{@code Long} stands for the userId.</p>
|
||||||
|
* <p>{@code String} stands for the packageName.</p>
|
||||||
|
*/
|
||||||
|
private Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
|
||||||
|
mAppUsagePeriodMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor when there exists battery level data.
|
* Constructor when there exists battery level data.
|
||||||
@@ -167,6 +173,12 @@ public class DataProcessManager {
|
|||||||
return mAppUsageEventList;
|
return mAppUsageEventList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
|
||||||
|
getAppUsagePeriodMap() {
|
||||||
|
return mAppUsagePeriodMap;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean getIsCurrentAppUsageLoaded() {
|
boolean getIsCurrentAppUsageLoaded() {
|
||||||
return mIsCurrentAppUsageLoaded;
|
return mIsCurrentAppUsageLoaded;
|
||||||
@@ -361,9 +373,10 @@ public class DataProcessManager {
|
|||||||
if (!mShowScreenOnTime) {
|
if (!mShowScreenOnTime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Sort the appUsageEventList in ascending order based on the timestamp.
|
// Generates the indexed AppUsagePeriod list data for each corresponding time slot for
|
||||||
Collections.sort(mAppUsageEventList, DataProcessor.TIMESTAMP_COMPARATOR);
|
// further use.
|
||||||
// TODO: process app usage data to an intermediate result for further use.
|
mAppUsagePeriodMap = DataProcessor.generateAppUsagePeriodMap(
|
||||||
|
mHourlyBatteryLevelsPerDay, mAppUsageEventList, mStartTimestampOfLevelData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToGenerateFinalDataAndApplyCallback() {
|
private void tryToGenerateFinalDataAndApplyCallback() {
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.fuelgauge.batteryusage;
|
package com.android.settings.fuelgauge.batteryusage;
|
||||||
|
|
||||||
|
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.getEffectivePackageName;
|
||||||
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTime;
|
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTime;
|
||||||
|
|
||||||
import android.app.usage.IUsageStatsManager;
|
import android.app.usage.IUsageStatsManager;
|
||||||
@@ -41,6 +42,7 @@ import android.os.UserHandle;
|
|||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
@@ -86,6 +88,10 @@ public final class DataProcessor {
|
|||||||
private static final BatteryHistEntry EMPTY_BATTERY_HIST_ENTRY =
|
private static final BatteryHistEntry EMPTY_BATTERY_HIST_ENTRY =
|
||||||
new BatteryHistEntry(new ContentValues());
|
new BatteryHistEntry(new ContentValues());
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final long DEFAULT_USAGE_DURATION_FOR_INCOMPLETE_INTERVAL =
|
||||||
|
DateUtils.SECOND_IN_MILLIS * 30;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int SELECTED_INDEX_ALL = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
static final int SELECTED_INDEX_ALL = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
|
||||||
@@ -222,6 +228,90 @@ public final class DataProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the indexed {@link AppUsagePeriod} list data for each corresponding time slot.
|
||||||
|
* Attributes the list of {@link AppUsageEvent} into hourly time slots and reformat them into
|
||||||
|
* {@link AppUsagePeriod} for easier use in the following process.
|
||||||
|
*
|
||||||
|
* <p>There could be 2 cases of the returned value:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>null: empty or invalid data.</li>
|
||||||
|
* <li>non-null: must be a 2d map and composed by:
|
||||||
|
* <p> [0][0] ~ [maxDailyIndex][maxHourlyIndex]</p></li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>The structure is consistent with the battery usage map returned by
|
||||||
|
* {@code getBatteryUsageMap}.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code Long} stands for the userId.</p>
|
||||||
|
* <p>{@code String} stands for the packageName.</p>
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
|
||||||
|
generateAppUsagePeriodMap(
|
||||||
|
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
||||||
|
final List<AppUsageEvent> appUsageEventList,
|
||||||
|
final long startTimestampOfLevelData) {
|
||||||
|
if (appUsageEventList.isEmpty()) {
|
||||||
|
Log.w(TAG, "appUsageEventList is empty");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Sorts the appUsageEventList in ascending order based on the timestamp before
|
||||||
|
// distribution.
|
||||||
|
Collections.sort(appUsageEventList, TIMESTAMP_COMPARATOR);
|
||||||
|
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> 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();
|
||||||
|
|
||||||
|
for (int dailyIndex = 0; dailyIndex < hourlyBatteryLevelsPerDay.size(); dailyIndex++) {
|
||||||
|
final Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>> dailyMap =
|
||||||
|
new HashMap<>();
|
||||||
|
resultMap.put(dailyIndex, dailyMap);
|
||||||
|
if (hourlyBatteryLevelsPerDay.get(dailyIndex) == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final List<Long> timestamps = hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Gets the app usage event list for this hourly slot first.
|
||||||
|
final List<AppUsageEvent> 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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The value could be null when there is no data in the hourly slot.
|
||||||
|
dailyMap.put(
|
||||||
|
hourlyIndex,
|
||||||
|
buildAppUsagePeriodList(
|
||||||
|
hourlyAppUsageEventList, startTimestamp, endTimestamp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the list of {@link AppUsageEvent} from the supplied {@link UsageEvents}.
|
* Generates the list of {@link AppUsageEvent} from the supplied {@link UsageEvents}.
|
||||||
*/
|
*/
|
||||||
@@ -498,12 +588,14 @@ public final class DataProcessor {
|
|||||||
/**
|
/**
|
||||||
* @return Returns the indexed battery usage data for each corresponding time slot.
|
* @return Returns the indexed battery usage data for each corresponding time slot.
|
||||||
*
|
*
|
||||||
* There could be 2 cases of the returned value:
|
* <p>There could be 2 cases of the returned value:</p>
|
||||||
* 1) null: empty or invalid data.
|
* <ul>
|
||||||
* 2) non-null: must be a 2d map and composed by 3 parts:
|
* <li>null: empty or invalid data.</li>
|
||||||
* 1 - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]
|
* <li>non-null: must be a 2d map and composed by 3 parts:</li>
|
||||||
* 2 - [0][SELECTED_INDEX_ALL] ~ [maxDailyIndex][SELECTED_INDEX_ALL]
|
* <p> 1 - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]</p>
|
||||||
* 3 - [0][0] ~ [maxDailyIndex][maxHourlyIndex]
|
* <p> 2 - [0][SELECTED_INDEX_ALL] ~ [maxDailyIndex][SELECTED_INDEX_ALL]</p>
|
||||||
|
* <p> 3 - [0][0] ~ [maxDailyIndex][maxHourlyIndex]</p>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -579,6 +671,140 @@ public final class DataProcessor {
|
|||||||
return new BatteryDiffData(appEntries, systemEntries);
|
return new BatteryDiffData(appEntries, systemEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@code Long} stands for the userId.</p>
|
||||||
|
* <p>{@code String} stands for the packageName.</p>
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
@Nullable
|
||||||
|
static Map<Long, Map<String, List<AppUsagePeriod>>> buildAppUsagePeriodList(
|
||||||
|
final List<AppUsageEvent> allAppUsageEvents, final long startTime, final long endTime) {
|
||||||
|
if (allAppUsageEvents.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes the list of AppUsagePeriod into device events and instance events for further
|
||||||
|
// use.
|
||||||
|
final List<AppUsageEvent> deviceEvents = new ArrayList<>();
|
||||||
|
final ArrayMap<Integer, List<AppUsageEvent>> usageEventsByInstanceId = new ArrayMap<>();
|
||||||
|
for (final AppUsageEvent event : allAppUsageEvents) {
|
||||||
|
final AppUsageEventType eventType = event.getType();
|
||||||
|
if (eventType == AppUsageEventType.ACTIVITY_RESUMED
|
||||||
|
|| eventType == AppUsageEventType.ACTIVITY_STOPPED) {
|
||||||
|
final int instanceId = event.getInstanceId();
|
||||||
|
if (usageEventsByInstanceId.get(instanceId) == null) {
|
||||||
|
usageEventsByInstanceId.put(instanceId, new ArrayList<>());
|
||||||
|
}
|
||||||
|
usageEventsByInstanceId.get(instanceId).add(event);
|
||||||
|
} else if (eventType == AppUsageEventType.DEVICE_SHUTDOWN) {
|
||||||
|
// Track device-wide events in their own list as they affect any app.
|
||||||
|
deviceEvents.add(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (usageEventsByInstanceId.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<Long, Map<String, List<AppUsagePeriod>>> allUsagePeriods = new HashMap<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < usageEventsByInstanceId.size(); i++) {
|
||||||
|
// The usage periods for an instance are determined by the usage events with its
|
||||||
|
// instance id and any device-wide events such as device shutdown.
|
||||||
|
final List<AppUsageEvent> usageEvents = usageEventsByInstanceId.valueAt(i);
|
||||||
|
if (usageEvents == null || usageEvents.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// The same instance must have same userId and packageName.
|
||||||
|
final AppUsageEvent firstEvent = usageEvents.get(0);
|
||||||
|
final long eventUserId = firstEvent.getUserId();
|
||||||
|
final String packageName = getEffectivePackageName(
|
||||||
|
sUsageStatsManager,
|
||||||
|
firstEvent.getPackageName(),
|
||||||
|
firstEvent.getTaskRootPackageName());
|
||||||
|
usageEvents.addAll(deviceEvents);
|
||||||
|
// Sorts the usageEvents in ascending order based on the timestamp before computing the
|
||||||
|
// period.
|
||||||
|
Collections.sort(usageEvents, TIMESTAMP_COMPARATOR);
|
||||||
|
|
||||||
|
// A package might have multiple instances. Computes the usage period per instance id
|
||||||
|
// and then merges them into the same user-package map.
|
||||||
|
final List<AppUsagePeriod> usagePeriodList =
|
||||||
|
buildAppUsagePeriodListPerInstance(usageEvents, startTime, endTime);
|
||||||
|
if (!usagePeriodList.isEmpty()) {
|
||||||
|
addToUsagePeriodMap(allUsagePeriods, usagePeriodList, eventUserId, packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorts all usage periods by start time.
|
||||||
|
for (final long userId : allUsagePeriods.keySet()) {
|
||||||
|
if (allUsagePeriods.get(userId) == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (final String packageName: allUsagePeriods.get(userId).keySet()) {
|
||||||
|
Collections.sort(
|
||||||
|
allUsagePeriods.get(userId).get(packageName),
|
||||||
|
Comparator.comparing(AppUsagePeriod::getStartTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allUsagePeriods.isEmpty() ? null : allUsagePeriods;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static List<AppUsagePeriod> buildAppUsagePeriodListPerInstance(
|
||||||
|
final List<AppUsageEvent> usageEvents, final long startTime, final long endTime) {
|
||||||
|
final List<AppUsagePeriod> 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()) {
|
||||||
|
pendingUsagePeriod.setStartTime(eventTime);
|
||||||
|
}
|
||||||
|
} 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we already have start time, add it directly.
|
||||||
|
addToPeriodList(usagePeriodList, pendingUsagePeriod.build());
|
||||||
|
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());
|
||||||
|
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()) {
|
||||||
|
pendingUsagePeriod.setEndTime(endTime);
|
||||||
|
addToPeriodList(usagePeriodList, pendingUsagePeriod.build());
|
||||||
|
pendingUsagePeriod.clear();
|
||||||
|
}
|
||||||
|
return usagePeriodList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the overall battery usage data from battery stats service directly.
|
* @return Returns the overall battery usage data from battery stats service directly.
|
||||||
*
|
*
|
||||||
@@ -613,6 +839,56 @@ public final class DataProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addToPeriodList(
|
||||||
|
final List<AppUsagePeriod> appUsagePeriodList, final AppUsagePeriod appUsagePeriod) {
|
||||||
|
// Only when the period is valid, add it into the list.
|
||||||
|
if (appUsagePeriod.getStartTime() < appUsagePeriod.getEndTime()) {
|
||||||
|
appUsagePeriodList.add(appUsagePeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addToUsagePeriodMap(
|
||||||
|
final Map<Long, Map<String, List<AppUsagePeriod>>> usagePeriodMap,
|
||||||
|
final List<AppUsagePeriod> usagePeriodList,
|
||||||
|
final long userId,
|
||||||
|
final String packageName) {
|
||||||
|
usagePeriodMap.computeIfAbsent(userId, key -> new HashMap<>());
|
||||||
|
final Map<String, List<AppUsagePeriod>> packageNameMap = usagePeriodMap.get(userId);
|
||||||
|
packageNameMap.computeIfAbsent(packageName, key -> new ArrayList<>());
|
||||||
|
packageNameMap.get(packageName).addAll(usagePeriodList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the start time that gives {@code usagePeriod} the default usage duration.
|
||||||
|
*/
|
||||||
|
private static long getStartTimeForIncompleteUsagePeriod(
|
||||||
|
final AppUsagePeriodOrBuilder usagePeriod) {
|
||||||
|
return usagePeriod.getEndTime() - DEFAULT_USAGE_DURATION_FOR_INCOMPLETE_INTERVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the end time that gives {@code usagePeriod} the default usage duration.
|
||||||
|
*/
|
||||||
|
private static long getEndTimeForIncompleteUsagePeriod(
|
||||||
|
final AppUsagePeriodOrBuilder usagePeriod, final long eventTime) {
|
||||||
|
return Math.min(
|
||||||
|
usagePeriod.getStartTime() + DEFAULT_USAGE_DURATION_FOR_INCOMPLETE_INTERVAL,
|
||||||
|
eventTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getFirstUsageEventIndex(
|
||||||
|
final List<AppUsageEvent> appUsageEventList,
|
||||||
|
final long startTimestampOfLevelData) {
|
||||||
|
int currentIndex = 0;
|
||||||
|
while (currentIndex < appUsageEventList.size()
|
||||||
|
&& (appUsageEventList.get(currentIndex) == null
|
||||||
|
|| appUsageEventList.get(currentIndex).getTimestamp()
|
||||||
|
< startTimestampOfLevelData)) {
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
return currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static UsageEvents getAppUsageEventsForUser(
|
private static UsageEvents getAppUsageEventsForUser(
|
||||||
Context context, final UserManager userManager, final int userID,
|
Context context, final UserManager userManager, final int userID,
|
||||||
|
@@ -32,3 +32,11 @@ message AppUsageEvent {
|
|||||||
optional int64 user_id = 6;
|
optional int64 user_id = 6;
|
||||||
optional int64 uid = 7;
|
optional int64 uid = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Represents a continuous period of time when an app is used.
|
||||||
|
message AppUsagePeriod {
|
||||||
|
// Start of the usage period.
|
||||||
|
optional int64 start_time = 1;
|
||||||
|
// End of the usage period.
|
||||||
|
optional int64 end_time = 2;
|
||||||
|
}
|
||||||
|
@@ -48,6 +48,7 @@ import org.robolectric.RobolectricTestRunner;
|
|||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -110,15 +111,31 @@ public final class DataProcessManagerTest {
|
|||||||
assertThat(mDataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
|
assertThat(mDataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
|
||||||
assertThat(mDataProcessManager.getShowScreenOnTime()).isTrue();
|
assertThat(mDataProcessManager.getShowScreenOnTime()).isTrue();
|
||||||
assertThat(mDataProcessManager.getAppUsageEventList()).isEmpty();
|
assertThat(mDataProcessManager.getAppUsageEventList()).isEmpty();
|
||||||
|
assertThat(mDataProcessManager.getAppUsagePeriodMap()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_loadExpectedAppUsageData() throws RemoteException {
|
public void start_loadExpectedAppUsageData() throws RemoteException {
|
||||||
|
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||||
|
new ArrayList<>();
|
||||||
|
final String packageName = "package";
|
||||||
|
// Adds the day 1 data.
|
||||||
|
final List<Long> timestamps1 = List.of(2L, 3L, 4L);
|
||||||
|
final List<Integer> 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<Long> timestamps2 = List.of(5L, 6L);
|
||||||
|
final List<Integer> levels2 = List.of(100, 100);
|
||||||
|
hourlyBatteryLevelsPerDay.add(
|
||||||
|
new BatteryLevelData.PeriodBatteryLevelData(timestamps2, levels2));
|
||||||
// Fake current usage data.
|
// Fake current usage data.
|
||||||
final UsageEvents.Event event1 =
|
final UsageEvents.Event event1 =
|
||||||
getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /*timestamp=*/ 1);
|
getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /*timestamp=*/ 1, packageName);
|
||||||
final UsageEvents.Event event2 =
|
final UsageEvents.Event event2 =
|
||||||
getUsageEvent(UsageEvents.Event.ACTIVITY_STOPPED, /*timestamp=*/ 2);
|
getUsageEvent(UsageEvents.Event.ACTIVITY_STOPPED, /*timestamp=*/ 2, packageName);
|
||||||
final List<UsageEvents.Event> events = new ArrayList<>();
|
final List<UsageEvents.Event> events = new ArrayList<>();
|
||||||
events.add(event1);
|
events.add(event1);
|
||||||
events.add(event2);
|
events.add(event2);
|
||||||
@@ -126,30 +143,46 @@ public final class DataProcessManagerTest {
|
|||||||
.when(mUsageStatsManager)
|
.when(mUsageStatsManager)
|
||||||
.queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
|
.queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
|
||||||
doReturn(true).when(mUserManager).isUserUnlocked(anyInt());
|
doReturn(true).when(mUserManager).isUserUnlocked(anyInt());
|
||||||
|
// Assign current user id.
|
||||||
|
doReturn(1).when(mContext).getUserId();
|
||||||
|
// No work profile.
|
||||||
|
doReturn(new ArrayList<>()).when(mUserManager).getUserProfiles();
|
||||||
|
|
||||||
// Fake database usage data.
|
// Fake database usage data.
|
||||||
final MatrixCursor cursor = new MatrixCursor(
|
final MatrixCursor cursor = new MatrixCursor(
|
||||||
new String[]{
|
new String[]{
|
||||||
AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE,
|
AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE,
|
||||||
AppUsageEventEntity.KEY_TIMESTAMP});
|
AppUsageEventEntity.KEY_TIMESTAMP,
|
||||||
|
AppUsageEventEntity.KEY_USER_ID,
|
||||||
|
AppUsageEventEntity.KEY_INSTANCE_ID,
|
||||||
|
AppUsageEventEntity.KEY_PACKAGE_NAME
|
||||||
|
});
|
||||||
// Adds fake data into the cursor.
|
// Adds fake data into the cursor.
|
||||||
cursor.addRow(new Object[] {
|
cursor.addRow(new Object[] {
|
||||||
AppUsageEventType.ACTIVITY_RESUMED.getNumber(), /*timestamp=*/ 3});
|
AppUsageEventType.ACTIVITY_RESUMED.getNumber(), /*timestamp=*/ 3, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName});
|
||||||
cursor.addRow(new Object[] {
|
cursor.addRow(new Object[] {
|
||||||
AppUsageEventType.ACTIVITY_RESUMED.getNumber(), /*timestamp=*/ 4});
|
AppUsageEventType.ACTIVITY_STOPPED.getNumber(), /*timestamp=*/ 4, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName});
|
||||||
cursor.addRow(new Object[] {
|
cursor.addRow(new Object[] {
|
||||||
AppUsageEventType.ACTIVITY_STOPPED.getNumber(), /*timestamp=*/ 5});
|
AppUsageEventType.ACTIVITY_RESUMED.getNumber(), /*timestamp=*/ 5, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName});
|
||||||
cursor.addRow(new Object[] {
|
cursor.addRow(new Object[] {
|
||||||
AppUsageEventType.ACTIVITY_STOPPED.getNumber(), /*timestamp=*/ 6});
|
AppUsageEventType.ACTIVITY_STOPPED.getNumber(), /*timestamp=*/ 6, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName});
|
||||||
DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
|
DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
|
||||||
|
|
||||||
mDataProcessManager.start();
|
final DataProcessManager dataProcessManager = new DataProcessManager(
|
||||||
|
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
|
||||||
|
hourlyBatteryLevelsPerDay, /*batteryHistoryMap=*/ null);
|
||||||
|
dataProcessManager.start();
|
||||||
|
|
||||||
assertThat(mDataProcessManager.getIsCurrentAppUsageLoaded()).isTrue();
|
assertThat(dataProcessManager.getIsCurrentAppUsageLoaded()).isTrue();
|
||||||
assertThat(mDataProcessManager.getIsDatabaseAppUsageLoaded()).isTrue();
|
assertThat(dataProcessManager.getIsDatabaseAppUsageLoaded()).isTrue();
|
||||||
assertThat(mDataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
|
assertThat(dataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
|
||||||
assertThat(mDataProcessManager.getShowScreenOnTime()).isTrue();
|
assertThat(dataProcessManager.getShowScreenOnTime()).isTrue();
|
||||||
final List<AppUsageEvent> appUsageEventList = mDataProcessManager.getAppUsageEventList();
|
final List<AppUsageEvent> appUsageEventList = dataProcessManager.getAppUsageEventList();
|
||||||
|
Collections.sort(appUsageEventList, DataProcessor.TIMESTAMP_COMPARATOR);
|
||||||
assertThat(appUsageEventList.size()).isEqualTo(6);
|
assertThat(appUsageEventList.size()).isEqualTo(6);
|
||||||
assertAppUsageEvent(
|
assertAppUsageEvent(
|
||||||
appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1);
|
appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1);
|
||||||
@@ -158,17 +191,41 @@ public final class DataProcessManagerTest {
|
|||||||
assertAppUsageEvent(
|
assertAppUsageEvent(
|
||||||
appUsageEventList.get(2), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 3);
|
appUsageEventList.get(2), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 3);
|
||||||
assertAppUsageEvent(
|
assertAppUsageEvent(
|
||||||
appUsageEventList.get(3), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 4);
|
appUsageEventList.get(3), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 4);
|
||||||
assertAppUsageEvent(
|
assertAppUsageEvent(
|
||||||
appUsageEventList.get(4), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 5);
|
appUsageEventList.get(4), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 5);
|
||||||
assertAppUsageEvent(
|
assertAppUsageEvent(
|
||||||
appUsageEventList.get(5), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 6);
|
appUsageEventList.get(5), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 6);
|
||||||
|
|
||||||
|
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
|
||||||
|
appUsagePeriodMap = dataProcessManager.getAppUsagePeriodMap();
|
||||||
|
assertThat(appUsagePeriodMap.size()).isEqualTo(3);
|
||||||
|
// Day 1
|
||||||
|
assertThat(appUsagePeriodMap.get(0).size()).isEqualTo(2);
|
||||||
|
Map<Long, Map<String, List<AppUsagePeriod>>> hourlyMap = appUsagePeriodMap.get(0).get(0);
|
||||||
|
assertThat(hourlyMap).isNull();
|
||||||
|
hourlyMap = appUsagePeriodMap.get(0).get(1);
|
||||||
|
assertThat(hourlyMap.size()).isEqualTo(1);
|
||||||
|
Map<String, List<AppUsagePeriod>> userMap = hourlyMap.get(1L);
|
||||||
|
assertThat(userMap.size()).isEqualTo(1);
|
||||||
|
assertThat(userMap.get(packageName).size()).isEqualTo(1);
|
||||||
|
assertAppUsagePeriod(userMap.get(packageName).get(0), 3, 4);
|
||||||
|
// Day 2
|
||||||
|
assertThat(appUsagePeriodMap.get(1).size()).isEqualTo(0);
|
||||||
|
// Day 3
|
||||||
|
assertThat(appUsagePeriodMap.get(2).size()).isEqualTo(1);
|
||||||
|
hourlyMap = appUsagePeriodMap.get(2).get(0);
|
||||||
|
assertThat(hourlyMap.size()).isEqualTo(1);
|
||||||
|
userMap = hourlyMap.get(1L);
|
||||||
|
assertThat(userMap.size()).isEqualTo(1);
|
||||||
|
assertThat(userMap.get(packageName).size()).isEqualTo(1);
|
||||||
|
assertAppUsagePeriod(userMap.get(packageName).get(0), 5, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_currentUserLocked_emptyAppUsageList() throws RemoteException {
|
public void start_currentUserLocked_emptyAppUsageList() throws RemoteException {
|
||||||
final UsageEvents.Event event =
|
final UsageEvents.Event event =
|
||||||
getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /*timestamp=*/ 1);
|
getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /*timestamp=*/ 1, "package");
|
||||||
final List<UsageEvents.Event> events = new ArrayList<>();
|
final List<UsageEvents.Event> events = new ArrayList<>();
|
||||||
events.add(event);
|
events.add(event);
|
||||||
doReturn(getUsageEvents(events))
|
doReturn(getUsageEvents(events))
|
||||||
@@ -187,6 +244,7 @@ public final class DataProcessManagerTest {
|
|||||||
mDataProcessManager.start();
|
mDataProcessManager.start();
|
||||||
|
|
||||||
assertThat(mDataProcessManager.getAppUsageEventList()).isEmpty();
|
assertThat(mDataProcessManager.getAppUsageEventList()).isEmpty();
|
||||||
|
assertThat(mDataProcessManager.getAppUsagePeriodMap()).isNull();
|
||||||
assertThat(mDataProcessManager.getShowScreenOnTime()).isFalse();
|
assertThat(mDataProcessManager.getShowScreenOnTime()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,10 +354,10 @@ public final class DataProcessManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private UsageEvents.Event getUsageEvent(
|
private UsageEvents.Event getUsageEvent(
|
||||||
final int eventType, final long timestamp) {
|
final int eventType, final long timestamp, final String packageName) {
|
||||||
final UsageEvents.Event event = new UsageEvents.Event();
|
final UsageEvents.Event event = new UsageEvents.Event();
|
||||||
event.mEventType = eventType;
|
event.mEventType = eventType;
|
||||||
event.mPackage = "package";
|
event.mPackage = packageName;
|
||||||
event.mTimeStamp = timestamp;
|
event.mTimeStamp = timestamp;
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
@@ -340,6 +398,12 @@ public final class DataProcessManagerTest {
|
|||||||
assertThat(event.getTimestamp()).isEqualTo(timestamp);
|
assertThat(event.getTimestamp()).isEqualTo(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertAppUsagePeriod(
|
||||||
|
final AppUsagePeriod period, final long startTime, final long endTime) {
|
||||||
|
assertThat(period.getStartTime()).isEqualTo(startTime);
|
||||||
|
assertThat(period.getEndTime()).isEqualTo(endTime);
|
||||||
|
}
|
||||||
|
|
||||||
private static void verifyExpectedBatteryLevelData(
|
private static void verifyExpectedBatteryLevelData(
|
||||||
final BatteryLevelData resultData,
|
final BatteryLevelData resultData,
|
||||||
final List<Long> expectedDailyTimestamps,
|
final List<Long> expectedDailyTimestamps,
|
||||||
|
@@ -175,7 +175,108 @@ public final class DataProcessorTest {
|
|||||||
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId, 0)).isNull();
|
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId, 0)).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void generateAppUsageEventListFromUsageEvents_returnExpectedResult() {
|
@Test
|
||||||
|
public void generateAppUsagePeriodMap_returnExpectedResult() {
|
||||||
|
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||||
|
new ArrayList<>();
|
||||||
|
final String packageName = "com.android.settings";
|
||||||
|
// Adds the day 1 data.
|
||||||
|
final List<Long> timestamps1 = List.of(10000L, 20000L, 30000L);
|
||||||
|
final List<Integer> 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<Long> timestamps2 = List.of(40000L, 50000L);
|
||||||
|
final List<Integer> levels2 = List.of(100, 100);
|
||||||
|
hourlyBatteryLevelsPerDay.add(
|
||||||
|
new BatteryLevelData.PeriodBatteryLevelData(timestamps2, levels2));
|
||||||
|
final long startTimestampOfLevelData = 10000L;
|
||||||
|
final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
|
||||||
|
// Adds some events before the start timestamp.
|
||||||
|
appUsageEventList.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName));
|
||||||
|
appUsageEventList.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 2, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName));
|
||||||
|
// Adds the valid app usage events.
|
||||||
|
appUsageEventList.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 10000L, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName));
|
||||||
|
appUsageEventList.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 15000L, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName));
|
||||||
|
appUsageEventList.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 12000L, /*userId=*/ 2,
|
||||||
|
/*instanceId=*/ 3, packageName));
|
||||||
|
appUsageEventList.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 18000L, /*userId=*/ 2,
|
||||||
|
/*instanceId=*/ 3, packageName));
|
||||||
|
appUsageEventList.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 35000L, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName));
|
||||||
|
appUsageEventList.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 45000L, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName));
|
||||||
|
appUsageEventList.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 42000L, /*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,
|
||||||
|
/*instanceId=*/ 4, packageName));
|
||||||
|
|
||||||
|
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> periodMap =
|
||||||
|
DataProcessor.generateAppUsagePeriodMap(
|
||||||
|
hourlyBatteryLevelsPerDay, appUsageEventList, startTimestampOfLevelData);
|
||||||
|
|
||||||
|
assertThat(periodMap.size()).isEqualTo(3);
|
||||||
|
// Day 1
|
||||||
|
assertThat(periodMap.get(0).size()).isEqualTo(2);
|
||||||
|
Map<Long, Map<String, List<AppUsagePeriod>>> hourlyMap = periodMap.get(0).get(0);
|
||||||
|
assertThat(hourlyMap.size()).isEqualTo(2);
|
||||||
|
Map<String, List<AppUsagePeriod>> userMap = hourlyMap.get(1L);
|
||||||
|
assertThat(userMap.size()).isEqualTo(1);
|
||||||
|
assertThat(userMap.get(packageName).size()).isEqualTo(1);
|
||||||
|
assertAppUsagePeriod(userMap.get(packageName).get(0), 10000, 15000);
|
||||||
|
userMap = hourlyMap.get(2L);
|
||||||
|
assertThat(userMap.size()).isEqualTo(1);
|
||||||
|
assertThat(userMap.get(packageName).size()).isEqualTo(1);
|
||||||
|
assertAppUsagePeriod(userMap.get(packageName).get(0), 12000, 18000);
|
||||||
|
hourlyMap = periodMap.get(0).get(1);
|
||||||
|
assertThat(hourlyMap).isNull();
|
||||||
|
// Day 2
|
||||||
|
assertThat(periodMap.get(1).size()).isEqualTo(0);
|
||||||
|
// Day 3
|
||||||
|
assertThat(periodMap.get(2).size()).isEqualTo(1);
|
||||||
|
hourlyMap = periodMap.get(2).get(0);
|
||||||
|
assertThat(hourlyMap.size()).isEqualTo(1);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateAppUsagePeriodMap_emptyEventList_returnNull() {
|
||||||
|
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||||
|
new ArrayList<>();
|
||||||
|
hourlyBatteryLevelsPerDay.add(
|
||||||
|
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
|
||||||
|
assertThat(DataProcessor.generateAppUsagePeriodMap(
|
||||||
|
hourlyBatteryLevelsPerDay, new ArrayList<>(), 0)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateAppUsageEventListFromUsageEvents_returnExpectedResult() {
|
||||||
Event event1 = getUsageEvent(Event.NOTIFICATION_INTERRUPTION, /*timestamp=*/ 1);
|
Event event1 = getUsageEvent(Event.NOTIFICATION_INTERRUPTION, /*timestamp=*/ 1);
|
||||||
Event event2 = getUsageEvent(Event.ACTIVITY_RESUMED, /*timestamp=*/ 2);
|
Event event2 = getUsageEvent(Event.ACTIVITY_RESUMED, /*timestamp=*/ 2);
|
||||||
Event event3 = getUsageEvent(Event.ACTIVITY_STOPPED, /*timestamp=*/ 3);
|
Event event3 = getUsageEvent(Event.ACTIVITY_STOPPED, /*timestamp=*/ 3);
|
||||||
@@ -1214,6 +1315,150 @@ public final class DataProcessorTest {
|
|||||||
/*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 10);
|
/*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildAppUsagePeriodList_returnExpectedResult() {
|
||||||
|
final List<AppUsageEvent> appUsageEvents = new ArrayList<>();
|
||||||
|
final String packageName1 = "com.android.settings1";
|
||||||
|
final String packageName2 = "com.android.settings2";
|
||||||
|
// Fake multiple instances in one package.
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName1));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 2, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName1));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 3, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName1));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 4, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 2, packageName1));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 2, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 3, packageName1));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 4, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 3, packageName1));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 2, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 5, packageName2));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 4, /*userId=*/ 1,
|
||||||
|
/*instanceId=*/ 5, packageName2));
|
||||||
|
// Fake one instance in one package.
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1, /*userId=*/ 2,
|
||||||
|
/*instanceId=*/ 4, packageName2));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 2, /*userId=*/ 2,
|
||||||
|
/*instanceId=*/ 4, packageName2));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 3, /*userId=*/ 2,
|
||||||
|
/*instanceId=*/ 4, packageName2));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 4, /*userId=*/ 2,
|
||||||
|
/*instanceId=*/ 4, packageName2));
|
||||||
|
|
||||||
|
final Map<Long, Map<String, List<AppUsagePeriod>>> appUsagePeriodMap =
|
||||||
|
DataProcessor.buildAppUsagePeriodList(appUsageEvents, 0, 5);
|
||||||
|
|
||||||
|
assertThat(appUsagePeriodMap.size()).isEqualTo(2);
|
||||||
|
final Map<String, List<AppUsagePeriod>> userMap1 = appUsagePeriodMap.get(1L);
|
||||||
|
assertThat(userMap1.size()).isEqualTo(2);
|
||||||
|
List<AppUsagePeriod> appUsagePeriodList = userMap1.get(packageName1);
|
||||||
|
assertThat(appUsagePeriodList.size()).isEqualTo(3);
|
||||||
|
assertAppUsagePeriod(appUsagePeriodList.get(0), 1, 2);
|
||||||
|
assertAppUsagePeriod(appUsagePeriodList.get(1), 2, 4);
|
||||||
|
assertAppUsagePeriod(appUsagePeriodList.get(2), 3, 4);
|
||||||
|
appUsagePeriodList = userMap1.get(packageName2);
|
||||||
|
assertThat(appUsagePeriodList.size()).isEqualTo(1);
|
||||||
|
assertAppUsagePeriod(appUsagePeriodList.get(0), 2, 4);
|
||||||
|
final Map<String, List<AppUsagePeriod>> userMap2 = appUsagePeriodMap.get(2L);
|
||||||
|
assertThat(userMap2.size()).isEqualTo(1);
|
||||||
|
appUsagePeriodList = userMap2.get(packageName2);
|
||||||
|
assertThat(appUsagePeriodList.size()).isEqualTo(2);
|
||||||
|
assertAppUsagePeriod(appUsagePeriodList.get(0), 1, 2);
|
||||||
|
assertAppUsagePeriod(appUsagePeriodList.get(1), 3, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildAppUsagePeriodList_emptyEventList_returnNull() {
|
||||||
|
assertThat(DataProcessor.buildAppUsagePeriodList(
|
||||||
|
new ArrayList<>(), 0, 1)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildAppUsagePeriodList_emptyActivityList_returnNull() {
|
||||||
|
final List<AppUsageEvent> appUsageEvents = new ArrayList<>();
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 1));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 2));
|
||||||
|
|
||||||
|
assertThat(DataProcessor.buildAppUsagePeriodList(
|
||||||
|
appUsageEvents, 0, 3)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildAppUsagePeriodListPerInstance_returnExpectedResult() {
|
||||||
|
final List<AppUsageEvent> appUsageEvents = new ArrayList<>();
|
||||||
|
// Fake normal data.
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 100000));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 200000));
|
||||||
|
// Fake two adjacent resume events.
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 300000));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 400000));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 500000));
|
||||||
|
// Fake no start event when stop event happens.
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 600000));
|
||||||
|
// There exists start event when device shutdown event happens. Shutdown is later than
|
||||||
|
// default complete time.
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 700000));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 800000));
|
||||||
|
// There exists start event when device shutdown event happens. Shutdown is earlier than
|
||||||
|
// default complete time.
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 900000));
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 910000));
|
||||||
|
// There exists start event when the period ends.
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1000000));
|
||||||
|
|
||||||
|
final List<AppUsagePeriod> appUsagePeriodList =
|
||||||
|
DataProcessor.buildAppUsagePeriodListPerInstance(appUsageEvents, 0, 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<AppUsageEvent> appUsageEvents = new ArrayList<>();
|
||||||
|
// Start with stop event.
|
||||||
|
appUsageEvents.add(buildAppUsageEvent(
|
||||||
|
AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 100000));
|
||||||
|
|
||||||
|
final List<AppUsagePeriod> appUsagePeriodList =
|
||||||
|
DataProcessor.buildAppUsagePeriodListPerInstance(appUsageEvents, 0, 200000);
|
||||||
|
|
||||||
|
assertThat(appUsageEvents.size()).isEqualTo(1);
|
||||||
|
assertAppUsagePeriod(appUsagePeriodList.get(0), 0, 100000);
|
||||||
|
}
|
||||||
|
|
||||||
private static Map<Long, Map<String, BatteryHistEntry>> createHistoryMap(
|
private static Map<Long, Map<String, BatteryHistEntry>> createHistoryMap(
|
||||||
final long[] timestamps, final int[] levels) {
|
final long[] timestamps, final int[] levels) {
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||||
@@ -1292,12 +1537,39 @@ public final class DataProcessorTest {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AppUsageEvent buildAppUsageEvent(final AppUsageEventType type, final long timestamp) {
|
||||||
|
return buildAppUsageEvent(
|
||||||
|
type, timestamp, /*userId=*/ 1, /*instanceId=*/ 2,
|
||||||
|
"com.android.settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppUsageEvent buildAppUsageEvent(
|
||||||
|
final AppUsageEventType type,
|
||||||
|
final long timestamp,
|
||||||
|
final long userId,
|
||||||
|
final int instanceId,
|
||||||
|
final String packageName) {
|
||||||
|
return AppUsageEvent.newBuilder()
|
||||||
|
.setType(type)
|
||||||
|
.setTimestamp(timestamp)
|
||||||
|
.setUserId(userId)
|
||||||
|
.setPackageName(packageName)
|
||||||
|
.setInstanceId(instanceId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private void assertAppUsageEvent(
|
private void assertAppUsageEvent(
|
||||||
final AppUsageEvent event, final AppUsageEventType eventType, final long timestamp) {
|
final AppUsageEvent event, final AppUsageEventType eventType, final long timestamp) {
|
||||||
assertThat(event.getType()).isEqualTo(eventType);
|
assertThat(event.getType()).isEqualTo(eventType);
|
||||||
assertThat(event.getTimestamp()).isEqualTo(timestamp);
|
assertThat(event.getTimestamp()).isEqualTo(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertAppUsagePeriod(
|
||||||
|
final AppUsagePeriod period, final long startTime, final long endTime) {
|
||||||
|
assertThat(period.getStartTime()).isEqualTo(startTime);
|
||||||
|
assertThat(period.getEndTime()).isEqualTo(endTime);
|
||||||
|
}
|
||||||
|
|
||||||
private static void verifyExpectedBatteryLevelData(
|
private static void verifyExpectedBatteryLevelData(
|
||||||
final BatteryLevelData resultData,
|
final BatteryLevelData resultData,
|
||||||
final List<Long> expectedDailyTimestamps,
|
final List<Long> expectedDailyTimestamps,
|
||||||
|
Reference in New Issue
Block a user