Implement the app usage data loading from database function.

Bug: 260964903
Test: make RunSettingsRoboTests + manual
Change-Id: I459dbdebe53e6b7421642955f36976b3e7c95fcb
This commit is contained in:
Kuan Wang
2022-12-19 19:59:13 +08:00
parent 092d07fa60
commit 97924455ff
12 changed files with 581 additions and 50 deletions

View File

@@ -24,10 +24,14 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.Utils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -63,9 +67,15 @@ public class DataProcessManager {
private final DataProcessor.UsageMapAsyncResponse mCallbackFunction;
private Context mContext;
private UserManager mUserManager;
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
// The start timestamp of battery level data. As we don't know when is the full charge cycle
// start time when loading app usage data, this value is used as the start time of querying app
// usage data.
private long mStartTimestampOfLevelData = 0;
private boolean mIsCurrentBatteryHistoryLoaded = false;
private boolean mIsCurrentAppUsageLoaded = false;
private boolean mIsDatabaseAppUsageLoaded = false;
@@ -81,13 +91,15 @@ public class DataProcessManager {
Context context,
Handler handler,
final DataProcessor.UsageMapAsyncResponse callbackFunction,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
@NonNull final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
@NonNull final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
mContext = context.getApplicationContext();
mHandler = handler;
mUserManager = mContext.getSystemService(UserManager.class);
mCallbackFunction = callbackFunction;
mHourlyBatteryLevelsPerDay = hourlyBatteryLevelsPerDay;
mBatteryHistoryMap = batteryHistoryMap;
mStartTimestampOfLevelData = getStartTimestampOfBatteryLevelData();
}
/**
@@ -102,6 +114,21 @@ public class DataProcessManager {
loadCurrentAppUsageList();
}
@VisibleForTesting
long getStartTimestampOfBatteryLevelData() {
for (int dailyIndex = 0; dailyIndex < mHourlyBatteryLevelsPerDay.size(); dailyIndex++) {
if (mHourlyBatteryLevelsPerDay.get(dailyIndex) == null) {
continue;
}
final List<Long> timestamps =
mHourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
if (timestamps.size() > 0) {
return timestamps.get(0);
}
}
return 0;
}
@VisibleForTesting
List<AppUsageEvent> getAppUsageEventList() {
return mAppUsageEventList;
@@ -164,12 +191,17 @@ public class DataProcessManager {
new AsyncTask<Void, Void, List<AppUsageEvent>>() {
@Override
protected List<AppUsageEvent> doInBackground(Void... voids) {
if (!shouldLoadAppUsageData()) {
Log.d(TAG, "not loadCurrentAppUsageList");
return null;
}
final long startTime = System.currentTimeMillis();
// Loads the current battery usage data from the battery stats service.
final int currentUserId = getCurrentUserId();
final int workProfileUserId = getWorkProfileUserId();
final UsageEvents usageEventsForCurrentUser =
DataProcessor.getAppUsageEventsForUser(mContext, currentUserId);
DataProcessor.getAppUsageEventsForUser(
mContext, currentUserId, mStartTimestampOfLevelData);
// If fail to load usage events for current user, return null directly and screen-on
// time will not be shown in the UI.
if (usageEventsForCurrentUser == null) {
@@ -180,7 +212,7 @@ public class DataProcessManager {
if (workProfileUserId != Integer.MIN_VALUE) {
usageEventsForWorkProfile =
DataProcessor.getAppUsageEventsForUser(
mContext, workProfileUserId);
mContext, workProfileUserId, mStartTimestampOfLevelData);
} else {
Log.d(TAG, "there is no work profile");
}
@@ -203,16 +235,8 @@ public class DataProcessManager {
@Override
protected void onPostExecute(
final List<AppUsageEvent> currentAppUsageList) {
final int currentUserId = getCurrentUserId();
final UserManager userManager = mContext.getSystemService(UserManager.class);
// If current user is locked, don't show screen-on time data in the UI.
// Even if we have data in the database, we won't show screen-on time because we
// don't have the latest data.
if (userManager == null || !userManager.isUserUnlocked(currentUserId)) {
Log.d(TAG, "current user is locked");
mShowScreenOnTime = false;
} else if (currentAppUsageList == null || currentAppUsageList.isEmpty()) {
Log.d(TAG, "usageEventsForWorkProfile is null or empty");
if (currentAppUsageList == null || currentAppUsageList.isEmpty()) {
Log.d(TAG, "currentAppUsageList is null or empty");
} else {
mAppUsageEventList.addAll(currentAppUsageList);
}
@@ -223,9 +247,36 @@ public class DataProcessManager {
}
private void loadDatabaseAppUsageList() {
// TODO: load app usage data from database.
mIsDatabaseAppUsageLoaded = true;
tryToProcessAppUsageData();
new AsyncTask<Void, Void, List<AppUsageEvent>>() {
@Override
protected List<AppUsageEvent> doInBackground(Void... voids) {
if (!shouldLoadAppUsageData()) {
Log.d(TAG, "not loadDatabaseAppUsageList");
return null;
}
final long startTime = System.currentTimeMillis();
// Loads the current battery usage data from the battery stats service.
final List<AppUsageEvent> appUsageEventList =
DatabaseUtils.getAppUsageEventForUsers(
mContext, Calendar.getInstance(), getCurrentUserIds(),
mStartTimestampOfLevelData);
Log.d(TAG, String.format("execute loadDatabaseAppUsageList size=%d in %d/ms",
appUsageEventList.size(), (System.currentTimeMillis() - startTime)));
return appUsageEventList;
}
@Override
protected void onPostExecute(
final List<AppUsageEvent> databaseAppUsageList) {
if (databaseAppUsageList == null || databaseAppUsageList.isEmpty()) {
Log.d(TAG, "databaseAppUsageList is null or empty");
} else {
mAppUsageEventList.addAll(databaseAppUsageList);
}
mIsDatabaseAppUsageLoaded = true;
tryToProcessAppUsageData();
}
}.execute();
}
private void tryToProcessAppUsageData() {
@@ -243,6 +294,8 @@ public class DataProcessManager {
if (!mShowScreenOnTime) {
return;
}
// Sort the appUsageEventList in ascending order based on the timestamp.
Collections.sort(mAppUsageEventList, DataProcessor.TIMESTAMP_COMPARATOR);
// TODO: process app usage data to an intermediate result for further use.
}
@@ -262,14 +315,39 @@ public class DataProcessManager {
// then apply the callback function.
}
// Whether we should load app usage data from service or database.
private boolean shouldLoadAppUsageData() {
if (!mShowScreenOnTime) {
return false;
}
final int currentUserId = getCurrentUserId();
// If current user is locked, no need to load app usage data from service or database.
if (mUserManager == null || !mUserManager.isUserUnlocked(currentUserId)) {
Log.d(TAG, "shouldLoadAppUsageData: false, current user is locked");
mShowScreenOnTime = false;
return false;
}
return true;
}
// Returns the list of current user id and work profile id if exists.
private List<Integer> getCurrentUserIds() {
final List<Integer> userIds = new ArrayList<>();
userIds.add(getCurrentUserId());
final int workProfileUserId = getWorkProfileUserId();
if (workProfileUserId != Integer.MIN_VALUE) {
userIds.add(workProfileUserId);
}
return userIds;
}
private int getCurrentUserId() {
return mContext.getUserId();
}
private int getWorkProfileUserId() {
final UserHandle userHandle =
Utils.getManagedProfile(
mContext.getSystemService(UserManager.class));
Utils.getManagedProfile(mUserManager);
return userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
}
}