Implement the app usage data loading from database function.
Bug: 260964903 Test: make RunSettingsRoboTests + manual Change-Id: I459dbdebe53e6b7421642955f36976b3e7c95fcb
This commit is contained in:
@@ -37,6 +37,9 @@ import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** {@link ContentProvider} class to fetch battery usage data. */
|
||||
public class BatteryUsageContentProvider extends ContentProvider {
|
||||
@@ -100,6 +103,8 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
||||
switch (sUriMatcher.match(uri)) {
|
||||
case BATTERY_STATE_CODE:
|
||||
return getBatteryStates(uri);
|
||||
case APP_USAGE_EVENT_CODE:
|
||||
return getAppUsageEvents(uri);
|
||||
case APP_USAGE_LATEST_TIMESTAMP_CODE:
|
||||
return getAppUsageLatestTimestamp(uri);
|
||||
default:
|
||||
@@ -153,8 +158,7 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
private Cursor getBatteryStates(Uri uri) {
|
||||
final long defaultTimestamp = mClock.millis() - QUERY_DURATION_HOURS.toMillis();
|
||||
final long queryTimestamp = getQueryTimestamp(uri, defaultTimestamp);
|
||||
final long queryTimestamp = getQueryTimestamp(uri);
|
||||
return getBatteryStates(uri, queryTimestamp);
|
||||
}
|
||||
|
||||
@@ -171,6 +175,24 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
private Cursor getAppUsageEvents(Uri uri) {
|
||||
final List<Long> queryUserIds = getQueryUserIds(uri);
|
||||
if (queryUserIds == null || queryUserIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final long queryTimestamp = getQueryTimestamp(uri);
|
||||
final long timestamp = mClock.millis();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = mAppUsageEventDao.getAllForUsersAfter(queryUserIds, queryTimestamp);
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, "query() from:" + uri + " error:" + e);
|
||||
}
|
||||
Log.w(TAG, "query app usage events in " + (mClock.millis() - timestamp) + "/ms");
|
||||
return cursor;
|
||||
|
||||
}
|
||||
|
||||
private Cursor getAppUsageLatestTimestamp(Uri uri) {
|
||||
final long queryUserId = getQueryUserId(uri);
|
||||
if (queryUserId == DatabaseUtils.INVALID_USER_ID) {
|
||||
@@ -188,6 +210,26 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
// If URI contains query parameter QUERY_KEY_USERID, use the value directly.
|
||||
// Otherwise, return null.
|
||||
private List<Long> getQueryUserIds(Uri uri) {
|
||||
Log.d(TAG, "getQueryUserIds from uri: " + uri);
|
||||
final String value = uri.getQueryParameter(DatabaseUtils.QUERY_KEY_USERID);
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
Log.w(TAG, "empty query value");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Arrays.asList(value.split(","))
|
||||
.stream()
|
||||
.map(s -> Long.parseLong(s.trim()))
|
||||
.collect(Collectors.toList());
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "invalid query value: " + value, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// If URI contains query parameter QUERY_KEY_USERID, use the value directly.
|
||||
// Otherwise, return INVALID_USER_ID.
|
||||
private long getQueryUserId(Uri uri) {
|
||||
@@ -198,8 +240,9 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
||||
|
||||
// If URI contains query parameter QUERY_KEY_TIMESTAMP, use the value directly.
|
||||
// Otherwise, load the data for QUERY_DURATION_HOURS by default.
|
||||
private long getQueryTimestamp(Uri uri, long defaultTimestamp) {
|
||||
private long getQueryTimestamp(Uri uri) {
|
||||
Log.d(TAG, "getQueryTimestamp from uri: " + uri);
|
||||
final long defaultTimestamp = mClock.millis() - QUERY_DURATION_HOURS.toMillis();
|
||||
return getQueryValueFromUri(uri, DatabaseUtils.QUERY_KEY_TIMESTAMP, defaultTimestamp);
|
||||
}
|
||||
|
||||
|
@@ -209,6 +209,25 @@ public final class ConvertUtils {
|
||||
return appUsageEventBuilder.build();
|
||||
}
|
||||
|
||||
/** Converts to {@link AppUsageEvent} from {@link Cursor} */
|
||||
public static AppUsageEvent convertToAppUsageEventFromCursor(final Cursor cursor) {
|
||||
final AppUsageEvent.Builder eventBuilder = AppUsageEvent.newBuilder();
|
||||
eventBuilder.setTimestamp(getLongFromCursor(cursor, AppUsageEventEntity.KEY_TIMESTAMP));
|
||||
eventBuilder.setType(
|
||||
AppUsageEventType.forNumber(
|
||||
getIntegerFromCursor(
|
||||
cursor, AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE)));
|
||||
eventBuilder.setPackageName(
|
||||
getStringFromCursor(cursor, AppUsageEventEntity.KEY_PACKAGE_NAME));
|
||||
eventBuilder.setInstanceId(
|
||||
getIntegerFromCursor(cursor, AppUsageEventEntity.KEY_INSTANCE_ID));
|
||||
eventBuilder.setTaskRootPackageName(
|
||||
getStringFromCursor(cursor, AppUsageEventEntity.KEY_TASK_ROOT_PACKAGE_NAME));
|
||||
eventBuilder.setUserId(getLongFromCursor(cursor, AppUsageEventEntity.KEY_USER_ID));
|
||||
eventBuilder.setUid(getLongFromCursor(cursor, AppUsageEventEntity.KEY_UID));
|
||||
return eventBuilder.build();
|
||||
}
|
||||
|
||||
/** Converts UTC timestamp to human readable local time string. */
|
||||
public static String utcToLocalTime(Context context, long timestamp) {
|
||||
final Locale locale = getLocale(context);
|
||||
@@ -331,4 +350,28 @@ public final class ConvertUtils {
|
||||
|
||||
return batteryInformationBuilder.build();
|
||||
}
|
||||
|
||||
private static int getIntegerFromCursor(final Cursor cursor, final String key) {
|
||||
final int columnIndex = cursor.getColumnIndex(key);
|
||||
if (columnIndex >= 0) {
|
||||
return cursor.getInt(columnIndex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long getLongFromCursor(final Cursor cursor, final String key) {
|
||||
final int columnIndex = cursor.getColumnIndex(key);
|
||||
if (columnIndex >= 0) {
|
||||
return cursor.getLong(columnIndex);
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
private static String getStringFromCursor(final Cursor cursor, final String key) {
|
||||
final int columnIndex = cursor.getColumnIndex(key);
|
||||
if (columnIndex >= 0) {
|
||||
return cursor.getString(columnIndex);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@@ -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,10 +247,37 @@ public class DataProcessManager {
|
||||
}
|
||||
|
||||
private void loadDatabaseAppUsageList() {
|
||||
// TODO: load app usage data from database.
|
||||
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() {
|
||||
// Only when all app usage events has been loaded, start processing app usage data to an
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -100,6 +100,8 @@ public final class DataProcessor {
|
||||
|
||||
public static final String CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER =
|
||||
"CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER";
|
||||
public static final Comparator<AppUsageEvent> TIMESTAMP_COMPARATOR =
|
||||
Comparator.comparing(AppUsageEvent::getTimestamp);
|
||||
|
||||
/** A callback listener when battery usage loading async task is executed. */
|
||||
public interface UsageMapAsyncResponse {
|
||||
@@ -228,7 +230,8 @@ public final class DataProcessor {
|
||||
* Gets the {@link UsageEvents} from system service for the specific user.
|
||||
*/
|
||||
@Nullable
|
||||
public static UsageEvents getAppUsageEventsForUser(Context context, final int userID) {
|
||||
public static UsageEvents getAppUsageEventsForUser(
|
||||
Context context, final int userID, final long startTimestampOfLevelData) {
|
||||
final long start = System.currentTimeMillis();
|
||||
context = DatabaseUtils.getOwnerContext(context);
|
||||
if (context == null) {
|
||||
@@ -240,8 +243,9 @@ public final class DataProcessor {
|
||||
}
|
||||
final long sixDaysAgoTimestamp =
|
||||
DatabaseUtils.getTimestampSixDaysAgo(Calendar.getInstance());
|
||||
final long earliestTimestamp = Math.max(sixDaysAgoTimestamp, startTimestampOfLevelData);
|
||||
final UsageEvents events = getAppUsageEventsForUser(
|
||||
context, userManager, userID, sixDaysAgoTimestamp);
|
||||
context, userManager, userID, earliestTimestamp);
|
||||
final long elapsedTime = System.currentTimeMillis() - start;
|
||||
Log.d(TAG, String.format("getAppUsageEventsForUser() for user %d in %d/ms",
|
||||
userID, elapsedTime));
|
||||
@@ -638,7 +642,7 @@ public final class DataProcessor {
|
||||
@Nullable
|
||||
private static UsageEvents getAppUsageEventsForUser(
|
||||
Context context, final UserManager userManager, final int userID,
|
||||
final long sixDaysAgoTimestamp) {
|
||||
final long earliestTimestamp) {
|
||||
final String callingPackage = context.getPackageName();
|
||||
final long now = System.currentTimeMillis();
|
||||
// When the user is not unlocked, UsageStatsManager will return null, so bypass the
|
||||
@@ -648,7 +652,7 @@ public final class DataProcessor {
|
||||
return null;
|
||||
}
|
||||
final long startTime = DatabaseUtils.getAppUsageStartTimestampOfUser(
|
||||
context, userID, sixDaysAgoTimestamp);
|
||||
context, userID, earliestTimestamp);
|
||||
return loadAppUsageEventsForUserFromService(
|
||||
sUsageStatsManager, startTime, now, userID, callingPackage);
|
||||
}
|
||||
|
@@ -46,6 +46,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** A utility class to operate battery usage database. */
|
||||
public final class DatabaseUtils {
|
||||
@@ -93,6 +94,8 @@ public final class DatabaseUtils {
|
||||
@VisibleForTesting
|
||||
static Supplier<Cursor> sFakeBatteryStateSupplier;
|
||||
@VisibleForTesting
|
||||
static Supplier<Cursor> sFakeAppUsageEventSupplier;
|
||||
@VisibleForTesting
|
||||
static Supplier<Cursor> sFakeAppUsageLatestTimestampSupplier;
|
||||
|
||||
private DatabaseUtils() {
|
||||
@@ -125,6 +128,38 @@ public final class DatabaseUtils {
|
||||
return Math.max(latestTimestamp, earliestTimestamp);
|
||||
}
|
||||
|
||||
/** Returns the current user data in app usage event table. */
|
||||
public static List<AppUsageEvent> getAppUsageEventForUsers(
|
||||
Context context,
|
||||
final Calendar calendar,
|
||||
final List<Integer> userIds,
|
||||
final long startTimestampOfLevelData) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final long sixDaysAgoTimestamp = getTimestampSixDaysAgo(calendar);
|
||||
final long queryTimestamp = Math.max(startTimestampOfLevelData, sixDaysAgoTimestamp);
|
||||
Log.d(TAG, "sixDayAgoTimestamp: " + sixDaysAgoTimestamp);
|
||||
final String queryUserIdString = userIds.stream()
|
||||
.map(userId -> String.valueOf(userId))
|
||||
.collect(Collectors.joining(","));
|
||||
// Builds the content uri everytime to avoid cache.
|
||||
final Uri appUsageEventUri =
|
||||
new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(AUTHORITY)
|
||||
.appendPath(APP_USAGE_EVENT_TABLE)
|
||||
.appendQueryParameter(
|
||||
QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
|
||||
.appendQueryParameter(QUERY_KEY_USERID, queryUserIdString)
|
||||
.build();
|
||||
|
||||
final List<AppUsageEvent> appUsageEventList =
|
||||
loadAppUsageEventsFromContentProvider(context, appUsageEventUri);
|
||||
Log.d(TAG, String.format("getAppUsageEventForUser userId=%s size=%d in %d/ms",
|
||||
queryUserIdString, appUsageEventList.size(),
|
||||
(System.currentTimeMillis() - startTime)));
|
||||
return appUsageEventList;
|
||||
}
|
||||
|
||||
/** Long: for timestamp and String: for BatteryHistEntry.getKey() */
|
||||
public static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceLastFullCharge(
|
||||
Context context, Calendar calendar) {
|
||||
@@ -357,6 +392,32 @@ public final class DatabaseUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<AppUsageEvent> loadAppUsageEventsFromContentProvider(
|
||||
Context context, Uri appUsageEventUri) {
|
||||
final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
|
||||
context = getOwnerContext(context);
|
||||
if (context == null) {
|
||||
return appUsageEventList;
|
||||
}
|
||||
try (Cursor cursor = sFakeAppUsageEventSupplier != null
|
||||
? sFakeAppUsageEventSupplier.get()
|
||||
: context.getContentResolver().query(appUsageEventUri, null, null, null)) {
|
||||
if (cursor == null || cursor.getCount() == 0) {
|
||||
return appUsageEventList;
|
||||
}
|
||||
// Loads and recovers all AppUsageEvent data from cursor.
|
||||
while (cursor.moveToNext()) {
|
||||
appUsageEventList.add(ConvertUtils.convertToAppUsageEventFromCursor(cursor));
|
||||
}
|
||||
try {
|
||||
cursor.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "cursor.close() failed", e);
|
||||
}
|
||||
}
|
||||
return appUsageEventList;
|
||||
}
|
||||
|
||||
private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
|
||||
Context context, Uri batteryStateUri) {
|
||||
context = DatabaseUtils.getOwnerContext(context);
|
||||
|
@@ -41,6 +41,11 @@ public interface AppUsageEventDao {
|
||||
@Query("SELECT * FROM AppUsageEventEntity WHERE timestamp > :timestamp ORDER BY timestamp DESC")
|
||||
List<AppUsageEventEntity> getAllAfter(long timestamp);
|
||||
|
||||
/** Gets the {@link Cursor} of all recorded data after a specific timestamp of the users. */
|
||||
@Query("SELECT * FROM AppUsageEventEntity WHERE timestamp >= :timestamp"
|
||||
+ " AND userId IN (:userIds) ORDER BY timestamp ASC")
|
||||
Cursor getAllForUsersAfter(List<Long> userIds, long timestamp);
|
||||
|
||||
/** Gets the {@link Cursor} of the latest timestamp of the specific user. */
|
||||
@Query("SELECT MAX(timestamp) as timestamp FROM AppUsageEventEntity WHERE userId = :userId")
|
||||
Cursor getLatestTimestampOfUser(long userId);
|
||||
|
@@ -43,13 +43,18 @@ import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.Shadows;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Tests for {@link BatteryUsageContentProvider}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public final class BatteryUsageContentProviderTest {
|
||||
private static final Uri VALID_BATTERY_STATE_CONTENT_URI = DatabaseUtils.BATTERY_CONTENT_URI;
|
||||
private static final long TIMESTAMP1 = System.currentTimeMillis();
|
||||
private static final long TIMESTAMP2 = System.currentTimeMillis() + 2;
|
||||
private static final long TIMESTAMP3 = System.currentTimeMillis() + 4;
|
||||
private static final String PACKAGE_NAME1 = "com.android.settings1";
|
||||
private static final String PACKAGE_NAME2 = "com.android.settings2";
|
||||
private static final String PACKAGE_NAME3 = "com.android.settings3";
|
||||
@@ -180,29 +185,49 @@ public final class BatteryUsageContentProviderTest {
|
||||
BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query_appUsageEvent_returnsExpectedResult() {
|
||||
insertAppUsageEvent();
|
||||
|
||||
final List<Long> userIds1 = new ArrayList<>();
|
||||
final long notExistingUserId = 3;
|
||||
userIds1.add(USER_ID1);
|
||||
userIds1.add(USER_ID2);
|
||||
userIds1.add(notExistingUserId);
|
||||
final Cursor cursor1 = getCursorOfAppUsage(userIds1, TIMESTAMP1);
|
||||
assertThat(cursor1.getCount()).isEqualTo(3);
|
||||
// Verifies the queried first battery state.
|
||||
cursor1.moveToFirst();
|
||||
assertThat(cursor1.getString(5 /*packageName*/)).isEqualTo(PACKAGE_NAME1);
|
||||
// Verifies the queried second battery state.
|
||||
cursor1.moveToNext();
|
||||
assertThat(cursor1.getString(5 /*packageName*/)).isEqualTo(PACKAGE_NAME2);
|
||||
// Verifies the queried third battery state.
|
||||
cursor1.moveToNext();
|
||||
assertThat(cursor1.getString(5 /*packageName*/)).isEqualTo(PACKAGE_NAME3);
|
||||
|
||||
final List<Long> userIds2 = new ArrayList<>();
|
||||
userIds2.add(USER_ID1);
|
||||
final Cursor cursor2 = getCursorOfAppUsage(userIds2, TIMESTAMP3);
|
||||
assertThat(cursor2.getCount()).isEqualTo(1);
|
||||
// Verifies the queried first battery state.
|
||||
cursor2.moveToFirst();
|
||||
assertThat(cursor2.getString(5 /*packageName*/)).isEqualTo(PACKAGE_NAME3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query_appUsageTimestamp_returnsExpectedResult() throws Exception {
|
||||
mProvider.onCreate();
|
||||
final long timestamp1 = System.currentTimeMillis();
|
||||
final long timestamp2 = timestamp1 + 2;
|
||||
final long timestamp3 = timestamp1 + 4;
|
||||
// Inserts some valid testing data.
|
||||
BatteryTestUtils.insertDataToAppUsageEventTable(
|
||||
mContext, USER_ID1, timestamp1, PACKAGE_NAME1);
|
||||
BatteryTestUtils.insertDataToAppUsageEventTable(
|
||||
mContext, USER_ID2, timestamp2, PACKAGE_NAME2);
|
||||
BatteryTestUtils.insertDataToAppUsageEventTable(
|
||||
mContext, USER_ID1, timestamp3, PACKAGE_NAME3);
|
||||
insertAppUsageEvent();
|
||||
|
||||
final Cursor cursor1 = getCursorOfLatestTimestamp(USER_ID1);
|
||||
assertThat(cursor1.getCount()).isEqualTo(1);
|
||||
cursor1.moveToFirst();
|
||||
assertThat(cursor1.getLong(0)).isEqualTo(timestamp3);
|
||||
assertThat(cursor1.getLong(0)).isEqualTo(TIMESTAMP3);
|
||||
|
||||
final Cursor cursor2 = getCursorOfLatestTimestamp(USER_ID2);
|
||||
assertThat(cursor2.getCount()).isEqualTo(1);
|
||||
cursor2.moveToFirst();
|
||||
assertThat(cursor2.getLong(0)).isEqualTo(timestamp2);
|
||||
assertThat(cursor2.getLong(0)).isEqualTo(TIMESTAMP2);
|
||||
|
||||
final long notExistingUserId = 3;
|
||||
final Cursor cursor3 = getCursorOfLatestTimestamp(notExistingUserId);
|
||||
@@ -383,6 +408,17 @@ public final class BatteryUsageContentProviderTest {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
private void insertAppUsageEvent() {
|
||||
mProvider.onCreate();
|
||||
// Inserts some valid testing data.
|
||||
BatteryTestUtils.insertDataToAppUsageEventTable(
|
||||
mContext, USER_ID1, TIMESTAMP1, PACKAGE_NAME1);
|
||||
BatteryTestUtils.insertDataToAppUsageEventTable(
|
||||
mContext, USER_ID2, TIMESTAMP2, PACKAGE_NAME2);
|
||||
BatteryTestUtils.insertDataToAppUsageEventTable(
|
||||
mContext, USER_ID1, TIMESTAMP3, PACKAGE_NAME3);
|
||||
}
|
||||
|
||||
private Cursor getCursorOfLatestTimestamp(final long userId) {
|
||||
final Uri appUsageLatestTimestampQueryContentUri =
|
||||
new Uri.Builder()
|
||||
@@ -400,4 +436,22 @@ public final class BatteryUsageContentProviderTest {
|
||||
/*strings1=*/ null,
|
||||
/*s1=*/ null);
|
||||
}
|
||||
|
||||
private Cursor getCursorOfAppUsage(final List<Long> userIds, final long queryTimestamp) {
|
||||
final String queryUserIdString = userIds.stream()
|
||||
.map(userId -> String.valueOf(userId))
|
||||
.collect(Collectors.joining(","));
|
||||
final Uri appUsageEventUri =
|
||||
new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(DatabaseUtils.AUTHORITY)
|
||||
.appendPath(DatabaseUtils.APP_USAGE_EVENT_TABLE)
|
||||
.appendQueryParameter(
|
||||
DatabaseUtils.QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
|
||||
.appendQueryParameter(DatabaseUtils.QUERY_KEY_USERID, queryUserIdString)
|
||||
.build();
|
||||
|
||||
return mProvider.query(
|
||||
appUsageEventUri, /*strings=*/ null, /*s=*/ null, /*strings1=*/ null, /*s1=*/ null);
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ import android.app.usage.UsageEvents.Event;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.MatrixCursor;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.BatteryUsageStats;
|
||||
import android.os.LocaleList;
|
||||
@@ -335,6 +336,68 @@ public final class ConvertUtilsTest {
|
||||
assertThat(appUsageEvent).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToAppUsageEventFromCursor_returnExpectedResult() {
|
||||
final MatrixCursor cursor = new MatrixCursor(
|
||||
new String[]{
|
||||
AppUsageEventEntity.KEY_UID,
|
||||
AppUsageEventEntity.KEY_USER_ID,
|
||||
AppUsageEventEntity.KEY_PACKAGE_NAME,
|
||||
AppUsageEventEntity.KEY_TIMESTAMP,
|
||||
AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE,
|
||||
AppUsageEventEntity.KEY_TASK_ROOT_PACKAGE_NAME,
|
||||
AppUsageEventEntity.KEY_INSTANCE_ID});
|
||||
cursor.addRow(
|
||||
new Object[]{
|
||||
101L,
|
||||
1001L,
|
||||
"com.android.settings1",
|
||||
10001L,
|
||||
AppUsageEventType.DEVICE_SHUTDOWN.getNumber(),
|
||||
"com.android.settings2",
|
||||
100001L});
|
||||
cursor.moveToFirst();
|
||||
|
||||
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEventFromCursor(cursor);
|
||||
|
||||
assertThat(appUsageEvent.getUid()).isEqualTo(101L);
|
||||
assertThat(appUsageEvent.getUserId()).isEqualTo(1001L);
|
||||
assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
|
||||
assertThat(appUsageEvent.getTimestamp()).isEqualTo(10001L);
|
||||
assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.DEVICE_SHUTDOWN);
|
||||
assertThat(appUsageEvent.getTaskRootPackageName()).isEqualTo("com.android.settings2");
|
||||
assertThat(appUsageEvent.getInstanceId()).isEqualTo(100001L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToAppUsageEventFromCursor_emptyInstanceIdAndRootName_returnExpectedResult() {
|
||||
final MatrixCursor cursor = new MatrixCursor(
|
||||
new String[]{
|
||||
AppUsageEventEntity.KEY_UID,
|
||||
AppUsageEventEntity.KEY_USER_ID,
|
||||
AppUsageEventEntity.KEY_PACKAGE_NAME,
|
||||
AppUsageEventEntity.KEY_TIMESTAMP,
|
||||
AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE});
|
||||
cursor.addRow(
|
||||
new Object[]{
|
||||
101L,
|
||||
1001L,
|
||||
"com.android.settings1",
|
||||
10001L,
|
||||
AppUsageEventType.DEVICE_SHUTDOWN.getNumber()});
|
||||
cursor.moveToFirst();
|
||||
|
||||
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEventFromCursor(cursor);
|
||||
|
||||
assertThat(appUsageEvent.getUid()).isEqualTo(101L);
|
||||
assertThat(appUsageEvent.getUserId()).isEqualTo(1001L);
|
||||
assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
|
||||
assertThat(appUsageEvent.getTimestamp()).isEqualTo(10001L);
|
||||
assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.DEVICE_SHUTDOWN);
|
||||
assertThat(appUsageEvent.getTaskRootPackageName()).isEqualTo("");
|
||||
assertThat(appUsageEvent.getInstanceId()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLocale_nullContext_returnDefaultLocale() {
|
||||
assertThat(ConvertUtils.getLocale(/*context=*/ null))
|
||||
|
@@ -27,10 +27,13 @@ import static org.mockito.Mockito.spy;
|
||||
import android.app.usage.IUsageStatsManager;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.content.Context;
|
||||
import android.database.MatrixCursor;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserManager;
|
||||
|
||||
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -65,11 +68,31 @@ public final class DataProcessManagerTest {
|
||||
|
||||
mDataProcessManager = new DataProcessManager(
|
||||
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
|
||||
/*hourlyBatteryLevelsPerDay=*/ null, /*batteryHistoryMap=*/ null);
|
||||
/*hourlyBatteryLevelsPerDay=*/ new ArrayList<>(), /*batteryHistoryMap=*/ null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void start_loadExpectedCurrentAppUsageData() throws RemoteException {
|
||||
public void start_loadEmptyDatabaseAppUsageData() {
|
||||
final MatrixCursor cursor = new MatrixCursor(
|
||||
new String[]{
|
||||
AppUsageEventEntity.KEY_UID,
|
||||
AppUsageEventEntity.KEY_PACKAGE_NAME,
|
||||
AppUsageEventEntity.KEY_TIMESTAMP});
|
||||
DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
|
||||
doReturn(true).when(mUserManager).isUserUnlocked(anyInt());
|
||||
|
||||
mDataProcessManager.start();
|
||||
|
||||
assertThat(mDataProcessManager.getIsCurrentAppUsageLoaded()).isTrue();
|
||||
assertThat(mDataProcessManager.getIsDatabaseAppUsageLoaded()).isTrue();
|
||||
assertThat(mDataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
|
||||
assertThat(mDataProcessManager.getShowScreenOnTime()).isTrue();
|
||||
assertThat(mDataProcessManager.getAppUsageEventList()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void start_loadExpectedAppUsageData() throws RemoteException {
|
||||
// Fake current usage data.
|
||||
final UsageEvents.Event event1 =
|
||||
getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /*timestamp=*/ 1);
|
||||
final UsageEvents.Event event2 =
|
||||
@@ -82,6 +105,22 @@ public final class DataProcessManagerTest {
|
||||
.queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
|
||||
doReturn(true).when(mUserManager).isUserUnlocked(anyInt());
|
||||
|
||||
// Fake database usage data.
|
||||
final MatrixCursor cursor = new MatrixCursor(
|
||||
new String[]{
|
||||
AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE,
|
||||
AppUsageEventEntity.KEY_TIMESTAMP});
|
||||
// Adds fake data into the cursor.
|
||||
cursor.addRow(new Object[] {
|
||||
AppUsageEventType.ACTIVITY_RESUMED.getNumber(), /*timestamp=*/ 3});
|
||||
cursor.addRow(new Object[] {
|
||||
AppUsageEventType.ACTIVITY_RESUMED.getNumber(), /*timestamp=*/ 4});
|
||||
cursor.addRow(new Object[] {
|
||||
AppUsageEventType.ACTIVITY_STOPPED.getNumber(), /*timestamp=*/ 5});
|
||||
cursor.addRow(new Object[] {
|
||||
AppUsageEventType.ACTIVITY_STOPPED.getNumber(), /*timestamp=*/ 6});
|
||||
DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
|
||||
|
||||
mDataProcessManager.start();
|
||||
|
||||
assertThat(mDataProcessManager.getIsCurrentAppUsageLoaded()).isTrue();
|
||||
@@ -89,11 +128,19 @@ public final class DataProcessManagerTest {
|
||||
assertThat(mDataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
|
||||
assertThat(mDataProcessManager.getShowScreenOnTime()).isTrue();
|
||||
final List<AppUsageEvent> appUsageEventList = mDataProcessManager.getAppUsageEventList();
|
||||
assertThat(appUsageEventList.size()).isEqualTo(2);
|
||||
assertThat(appUsageEventList.size()).isEqualTo(6);
|
||||
assertAppUsageEvent(
|
||||
appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1);
|
||||
assertAppUsageEvent(
|
||||
appUsageEventList.get(1), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 2);
|
||||
assertAppUsageEvent(
|
||||
appUsageEventList.get(2), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 3);
|
||||
assertAppUsageEvent(
|
||||
appUsageEventList.get(3), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 4);
|
||||
assertAppUsageEvent(
|
||||
appUsageEventList.get(4), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 5);
|
||||
assertAppUsageEvent(
|
||||
appUsageEventList.get(5), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 6);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -106,6 +153,14 @@ public final class DataProcessManagerTest {
|
||||
.when(mUsageStatsManager)
|
||||
.queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
|
||||
doReturn(false).when(mUserManager).isUserUnlocked(anyInt());
|
||||
final MatrixCursor cursor = new MatrixCursor(
|
||||
new String[]{
|
||||
AppUsageEventEntity.KEY_UID,
|
||||
AppUsageEventEntity.KEY_PACKAGE_NAME,
|
||||
AppUsageEventEntity.KEY_TIMESTAMP});
|
||||
// Adds fake data into the cursor.
|
||||
cursor.addRow(new Object[] {101L, "app name1", 1001L});
|
||||
DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
|
||||
|
||||
mDataProcessManager.start();
|
||||
|
||||
@@ -113,6 +168,42 @@ public final class DataProcessManagerTest {
|
||||
assertThat(mDataProcessManager.getShowScreenOnTime()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStartTimestampOfBatteryLevelData_returnExpectedResult() {
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
final List<Long> timestamps = new ArrayList<>();
|
||||
timestamps.add(101L);
|
||||
timestamps.add(1001L);
|
||||
final List<Integer> levels = new ArrayList<>();
|
||||
levels.add(1);
|
||||
levels.add(2);
|
||||
hourlyBatteryLevelsPerDay.add(null);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
|
||||
|
||||
final DataProcessManager dataProcessManager = new DataProcessManager(
|
||||
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
|
||||
hourlyBatteryLevelsPerDay, /*batteryHistoryMap=*/ null);
|
||||
|
||||
assertThat(dataProcessManager.getStartTimestampOfBatteryLevelData()).isEqualTo(101);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStartTimestampOfBatteryLevelData_emptyLevels_returnZero() {
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
hourlyBatteryLevelsPerDay.add(null);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
|
||||
|
||||
final DataProcessManager dataProcessManager = new DataProcessManager(
|
||||
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
|
||||
hourlyBatteryLevelsPerDay, /*batteryHistoryMap=*/ null);
|
||||
|
||||
assertThat(dataProcessManager.getStartTimestampOfBatteryLevelData()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private UsageEvents getUsageEvents(final List<UsageEvents.Event> events) {
|
||||
UsageEvents usageEvents = new UsageEvents(events, new String[] {"package"});
|
||||
Parcel parcel = Parcel.obtain();
|
||||
|
@@ -213,7 +213,7 @@ public final class DataProcessorTest {
|
||||
.when(mUsageStatsManager)
|
||||
.queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
|
||||
|
||||
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId))
|
||||
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId, 0))
|
||||
.isEqualTo(mUsageEvents1);
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ public final class DataProcessorTest {
|
||||
// Test locked user.
|
||||
doReturn(false).when(mUserManager).isUserUnlocked(userId);
|
||||
|
||||
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId)).isNull();
|
||||
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId, 0)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -233,7 +233,7 @@ public final class DataProcessorTest {
|
||||
doReturn(null)
|
||||
.when(mUsageStatsManager).queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
|
||||
|
||||
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId)).isNull();
|
||||
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId, 0)).isNull();
|
||||
}
|
||||
|
||||
@Test public void generateAppUsageEventListFromUsageEvents_returnExpectedResult() {
|
||||
@@ -647,6 +647,7 @@ public final class DataProcessorTest {
|
||||
public void getBatteryUsageMap_emptyHistoryMap_returnNull() {
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
|
||||
|
||||
|
@@ -276,6 +276,62 @@ public final class DatabaseUtilsTest {
|
||||
mContext, /*userId=*/ 0, earliestTimestamp2)).isEqualTo(earliestTimestamp2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAppUsageEventForUsers_emptyCursorContent_returnEmptyMap() {
|
||||
final MatrixCursor cursor = new MatrixCursor(
|
||||
new String[]{
|
||||
AppUsageEventEntity.KEY_UID,
|
||||
AppUsageEventEntity.KEY_USER_ID,
|
||||
AppUsageEventEntity.KEY_PACKAGE_NAME,
|
||||
AppUsageEventEntity.KEY_TIMESTAMP,
|
||||
AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE});
|
||||
DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
|
||||
|
||||
assertThat(DatabaseUtils.getAppUsageEventForUsers(
|
||||
mContext,
|
||||
/*calendar=*/ null,
|
||||
/*userIds=*/ new ArrayList<>(),
|
||||
/*startTimestampOfLevelData=*/ 0)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAppUsageEventForUsers_nullCursor_returnEmptyMap() {
|
||||
DatabaseUtils.sFakeAppUsageEventSupplier = () -> null;
|
||||
assertThat(DatabaseUtils.getAppUsageEventForUsers(
|
||||
mContext,
|
||||
/*calendar=*/ null,
|
||||
/*userIds=*/ new ArrayList<>(),
|
||||
/*startTimestampOfLevelData=*/ 0)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAppUsageEventForUsers_returnExpectedMap() {
|
||||
final Long timestamp1 = 1001L;
|
||||
final Long timestamp2 = 1002L;
|
||||
final MatrixCursor cursor = new MatrixCursor(
|
||||
new String[]{
|
||||
AppUsageEventEntity.KEY_UID,
|
||||
AppUsageEventEntity.KEY_PACKAGE_NAME,
|
||||
AppUsageEventEntity.KEY_TIMESTAMP});
|
||||
// Adds fake data into the cursor.
|
||||
cursor.addRow(new Object[] {101L, "app name1", timestamp1});
|
||||
cursor.addRow(new Object[] {101L, "app name2", timestamp2});
|
||||
cursor.addRow(new Object[] {101L, "app name3", timestamp2});
|
||||
cursor.addRow(new Object[] {101L, "app name4", timestamp2});
|
||||
DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
|
||||
|
||||
final List<AppUsageEvent> appUsageEventList = DatabaseUtils.getAppUsageEventForUsers(
|
||||
mContext,
|
||||
/*calendar=*/ null,
|
||||
/*userIds=*/ new ArrayList<>(),
|
||||
/*startTimestampOfLevelData=*/ 0);
|
||||
|
||||
assertThat(appUsageEventList.get(0).getPackageName()).isEqualTo("app name1");
|
||||
assertThat(appUsageEventList.get(1).getPackageName()).isEqualTo("app name2");
|
||||
assertThat(appUsageEventList.get(2).getPackageName()).isEqualTo("app name3");
|
||||
assertThat(appUsageEventList.get(3).getPackageName()).isEqualTo("app name4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHistoryMapSinceLastFullCharge_emptyCursorContent_returnEmptyMap() {
|
||||
final MatrixCursor cursor = new MatrixCursor(
|
||||
|
@@ -31,11 +31,13 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Tests for {@link AppUsageEventDao}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public final class AppUsageEventDaoTest {
|
||||
private static final int CURSOR_COLUMN_SIZE = 8;
|
||||
private static final long TIMESTAMP1 = System.currentTimeMillis();
|
||||
private static final long TIMESTAMP2 = System.currentTimeMillis() + 2;
|
||||
private static final long TIMESTAMP3 = System.currentTimeMillis() + 4;
|
||||
@@ -77,6 +79,36 @@ public final class AppUsageEventDaoTest {
|
||||
assertAppUsageEvent(entities.get(1), TIMESTAMP2, PACKAGE_NAME2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appUsageEventDao_getAllForUsersAfter() {
|
||||
final List<Long> userIds1 = new ArrayList<>();
|
||||
final long notExistingUserId = 3;
|
||||
userIds1.add(USER_ID1);
|
||||
userIds1.add(USER_ID2);
|
||||
userIds1.add(notExistingUserId);
|
||||
final Cursor cursor1 = mAppUsageEventDao.getAllForUsersAfter(userIds1, TIMESTAMP1);
|
||||
assertThat(cursor1.getCount()).isEqualTo(3);
|
||||
assertThat(cursor1.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
|
||||
// Verifies the queried first battery state.
|
||||
cursor1.moveToFirst();
|
||||
assertThat(cursor1.getString(5 /*packageName*/)).isEqualTo(PACKAGE_NAME1);
|
||||
// Verifies the queried second battery state.
|
||||
cursor1.moveToNext();
|
||||
assertThat(cursor1.getString(5 /*packageName*/)).isEqualTo(PACKAGE_NAME2);
|
||||
// Verifies the queried third battery state.
|
||||
cursor1.moveToNext();
|
||||
assertThat(cursor1.getString(5 /*packageName*/)).isEqualTo(PACKAGE_NAME3);
|
||||
|
||||
final List<Long> userIds2 = new ArrayList<>();
|
||||
userIds2.add(USER_ID1);
|
||||
final Cursor cursor2 = mAppUsageEventDao.getAllForUsersAfter(userIds2, TIMESTAMP3);
|
||||
assertThat(cursor2.getCount()).isEqualTo(1);
|
||||
assertThat(cursor2.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
|
||||
// Verifies the queried first battery state.
|
||||
cursor2.moveToFirst();
|
||||
assertThat(cursor2.getString(5 /*packageName*/)).isEqualTo(PACKAGE_NAME3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appUsageEventDao_getLatestTimestampOfUser() throws Exception {
|
||||
final Cursor cursor1 = mAppUsageEventDao.getLatestTimestampOfUser(USER_ID1);
|
||||
|
Reference in New Issue
Block a user