Add DataProcessManager to manage the async tasks of battery usage data

processing.

Test: make RunSettingsRoboTests + manually
Bug: 260964903
Change-Id: Id3b2772a98ec2ab3b03910c8a5e81adf7ccd5646
This commit is contained in:
Kuan Wang
2022-12-15 16:43:25 +08:00
parent 16e6c4abb6
commit 8d2a26caed
5 changed files with 531 additions and 52 deletions

View File

@@ -0,0 +1,275 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage;
import android.app.usage.UsageEvents;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.Utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Manages the async tasks to process battery and app usage data.
*
* For now, there exist 3 async tasks in this manager:
* <ul>
* <li>loadCurrentBatteryHistoryMap: load the latest battery history data from battery stats
* service.</li>
* <li>loadCurrentAppUsageList: load the latest app usage data (last timestamp in database - now)
* from usage stats service.</li>
* <li>loadDatabaseAppUsageList: load the necessary app usage data (after last full charge) from
* database</li>
* </ul>
*
* The 3 async tasks will be started at the same time.
* <ul>
* <li>After loadCurrentAppUsageList and loadDatabaseAppUsageList complete, which means all app
* usage data has been loaded, the intermediate usage result will be generated.</li>
* <li>Then after all 3 async tasks complete, the battery history data and app usage data will be
* combined to generate final data used for UI rendering. And the callback function will be
* applied.</li>
* <li>If current user is locked, which means we couldn't get the latest app usage data,
* screen-on time will not be shown in the UI and empty screen-on time data will be returned.</li>
* </ul>
*/
public class DataProcessManager {
private static final String TAG = "DataProcessManager";
private final Handler mHandler;
private final DataProcessor.UsageMapAsyncResponse mCallbackFunction;
private Context mContext;
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
private boolean mIsCurrentBatteryHistoryLoaded = false;
private boolean mIsCurrentAppUsageLoaded = false;
private boolean mIsDatabaseAppUsageLoaded = false;
// Used to identify whether screen-on time data should be shown in the UI.
private boolean mShowScreenOnTime = true;
private List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
/**
* Constructor when this exists battery level data.
*/
DataProcessManager(
Context context,
Handler handler,
final DataProcessor.UsageMapAsyncResponse callbackFunction,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
mContext = context.getApplicationContext();
mHandler = handler;
mCallbackFunction = callbackFunction;
mHourlyBatteryLevelsPerDay = hourlyBatteryLevelsPerDay;
mBatteryHistoryMap = batteryHistoryMap;
}
/**
* Starts the async tasks to load battery history data and app usage data.
*/
public void start() {
// Load the latest battery history data from the service.
loadCurrentBatteryHistoryMap();
// Load app usage list from database.
loadDatabaseAppUsageList();
// Load the latest app usage list from the service.
loadCurrentAppUsageList();
}
@VisibleForTesting
List<AppUsageEvent> getAppUsageEventList() {
return mAppUsageEventList;
}
@VisibleForTesting
boolean getIsCurrentAppUsageLoaded() {
return mIsCurrentAppUsageLoaded;
}
@VisibleForTesting
boolean getIsDatabaseAppUsageLoaded() {
return mIsDatabaseAppUsageLoaded;
}
@VisibleForTesting
boolean getIsCurrentBatteryHistoryLoaded() {
return mIsCurrentBatteryHistoryLoaded;
}
@VisibleForTesting
boolean getShowScreenOnTime() {
return mShowScreenOnTime;
}
private void loadCurrentBatteryHistoryMap() {
new AsyncTask<Void, Void, Map<String, BatteryHistEntry>>() {
@Override
protected Map<String, BatteryHistEntry> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
// Loads the current battery usage data from the battery stats service.
final Map<String, BatteryHistEntry> currentBatteryHistoryMap =
DataProcessor.getCurrentBatteryHistoryMapFromStatsService(
mContext);
Log.d(TAG, String.format("execute loadCurrentBatteryHistoryMap size=%d in %d/ms",
currentBatteryHistoryMap.size(), (System.currentTimeMillis() - startTime)));
return currentBatteryHistoryMap;
}
@Override
protected void onPostExecute(
final Map<String, BatteryHistEntry> currentBatteryHistoryMap) {
if (mBatteryHistoryMap != null) {
// Replaces the placeholder in mBatteryHistoryMap.
for (Map.Entry<Long, Map<String, BatteryHistEntry>> mapEntry
: mBatteryHistoryMap.entrySet()) {
if (mapEntry.getValue().containsKey(
DataProcessor.CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER)) {
mapEntry.setValue(currentBatteryHistoryMap);
}
}
}
mIsCurrentBatteryHistoryLoaded = true;
tryToGenerateFinalDataAndApplyCallback();
}
}.execute();
}
private void loadCurrentAppUsageList() {
new AsyncTask<Void, Void, List<AppUsageEvent>>() {
@Override
protected List<AppUsageEvent> doInBackground(Void... voids) {
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);
// 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) {
Log.w(TAG, "usageEventsForCurrentUser is null");
return null;
}
UsageEvents usageEventsForWorkProfile = null;
if (workProfileUserId != Integer.MIN_VALUE) {
usageEventsForWorkProfile =
DataProcessor.getAppUsageEventsForUser(
mContext, workProfileUserId);
} else {
Log.d(TAG, "there is no work profile");
}
final Map<Long, UsageEvents> usageEventsMap = new HashMap<>();
usageEventsMap.put(Long.valueOf(currentUserId), usageEventsForCurrentUser);
if (usageEventsForWorkProfile != null) {
Log.d(TAG, "usageEventsForWorkProfile is null");
usageEventsMap.put(Long.valueOf(workProfileUserId), usageEventsForWorkProfile);
}
final List<AppUsageEvent> appUsageEventList =
DataProcessor.generateAppUsageEventListFromUsageEvents(
mContext, usageEventsMap);
Log.d(TAG, String.format("execute loadCurrentAppUsageList size=%d in %d/ms",
appUsageEventList.size(), (System.currentTimeMillis() - startTime)));
return appUsageEventList;
}
@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");
} else {
mAppUsageEventList.addAll(currentAppUsageList);
}
mIsCurrentAppUsageLoaded = true;
tryToProcessAppUsageData();
}
}.execute();
}
private void loadDatabaseAppUsageList() {
// TODO: load app usage data from database.
mIsDatabaseAppUsageLoaded = true;
tryToProcessAppUsageData();
}
private void tryToProcessAppUsageData() {
// Only when all app usage events has been loaded, start processing app usage data to an
// intermediate result for further use.
if (!mIsCurrentAppUsageLoaded || !mIsDatabaseAppUsageLoaded) {
return;
}
processAppUsageData();
tryToGenerateFinalDataAndApplyCallback();
}
private void processAppUsageData() {
// If there is no screen-on time data, no need to process.
if (!mShowScreenOnTime) {
return;
}
// TODO: process app usage data to an intermediate result for further use.
}
private void tryToGenerateFinalDataAndApplyCallback() {
// Only when both battery history data and app usage events data has been loaded, start the
// final data processing.
if (!mIsCurrentBatteryHistoryLoaded
|| !mIsCurrentAppUsageLoaded
|| !mIsDatabaseAppUsageLoaded) {
return;
}
generateFinalDataAndApplyCallback();
}
private void generateFinalDataAndApplyCallback() {
// TODO: generate the final data including battery usage map and device screen-on time and
// then apply the callback function.
}
private int getCurrentUserId() {
return mContext.getUserId();
}
private int getWorkProfileUserId() {
final UserHandle userHandle =
Utils.getManagedProfile(
mContext.getSystemService(UserManager.class));
return userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
}
}

View File

@@ -89,9 +89,6 @@ public final class DataProcessor {
@VisibleForTesting
static final int SELECTED_INDEX_ALL = BatteryChartViewModel.SELECTED_INDEX_ALL;
@VisibleForTesting
static final String CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER =
"CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER";
@VisibleForTesting
static long sFakeCurrentTimeMillis = 0;
@@ -101,6 +98,9 @@ public final class DataProcessor {
IUsageStatsManager.Stub.asInterface(
ServiceManager.getService(Context.USAGE_STATS_SERVICE));
public static final String CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER =
"CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER";
/** A callback listener when battery usage loading async task is executed. */
public interface UsageMapAsyncResponse {
/** The callback function when batteryUsageMap is loaded. */
@@ -200,18 +200,9 @@ public final class DataProcessor {
@Nullable
public static Map<Long, UsageEvents> getAppUsageEvents(Context context) {
final long start = System.currentTimeMillis();
final boolean isWorkProfileUser = DatabaseUtils.isWorkProfile(context);
Log.d(TAG, "getAppUsageEvents() isWorkProfileUser:" + isWorkProfileUser);
if (isWorkProfileUser) {
try {
context = context.createPackageContextAsUser(
/*packageName=*/ context.getPackageName(),
/*flags=*/ 0,
/*user=*/ UserHandle.OWNER);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "context.createPackageContextAsUser() fail:" + e);
return null;
}
context = DatabaseUtils.getOwnerContext(context);
if (context == null) {
return null;
}
final Map<Long, UsageEvents> resultMap = new HashMap();
final UserManager userManager = context.getSystemService(UserManager.class);
@@ -220,19 +211,9 @@ public final class DataProcessor {
}
final long sixDaysAgoTimestamp =
DatabaseUtils.getTimestampSixDaysAgo(Calendar.getInstance());
final String callingPackage = context.getPackageName();
final long now = System.currentTimeMillis();
for (final UserInfo user : userManager.getAliveUsers()) {
// When the user is not unlocked, UsageStatsManager will return null, so bypass the
// following data loading logics directly.
if (!userManager.isUserUnlocked(user.id)) {
Log.w(TAG, "fail to load app usage event for user :" + user.id + " because locked");
continue;
}
final long startTime = DatabaseUtils.getAppUsageStartTimestampOfUser(
context, user.id, sixDaysAgoTimestamp);
final UsageEvents events = getAppUsageEventsForUser(
sUsageStatsManager, startTime, now, user.id, callingPackage);
context, userManager, user.id, sixDaysAgoTimestamp);
if (events != null) {
resultMap.put(Long.valueOf(user.id), events);
}
@@ -243,6 +224,30 @@ public final class DataProcessor {
return resultMap.isEmpty() ? null : resultMap;
}
/**
* Gets the {@link UsageEvents} from system service for the specific user.
*/
@Nullable
public static UsageEvents getAppUsageEventsForUser(Context context, final int userID) {
final long start = System.currentTimeMillis();
context = DatabaseUtils.getOwnerContext(context);
if (context == null) {
return null;
}
final UserManager userManager = context.getSystemService(UserManager.class);
if (userManager == null) {
return null;
}
final long sixDaysAgoTimestamp =
DatabaseUtils.getTimestampSixDaysAgo(Calendar.getInstance());
final UsageEvents events = getAppUsageEventsForUser(
context, userManager, userID, sixDaysAgoTimestamp);
final long elapsedTime = System.currentTimeMillis() - start;
Log.d(TAG, String.format("getAppUsageEventsForUser() for user %d in %d/ms",
userID, elapsedTime));
return events;
}
/**
* Closes the {@link BatteryUsageStats} after using it.
*/
@@ -335,6 +340,17 @@ public final class DataProcessor {
return usageList;
}
/**
* @return Returns the latest battery history map loaded from the battery stats service.
*/
public static Map<String, BatteryHistEntry> getCurrentBatteryHistoryMapFromStatsService(
final Context context) {
final List<BatteryHistEntry> batteryHistEntryList =
getBatteryHistListFromFromStatsService(context);
return batteryHistEntryList == null ? new HashMap<>()
: batteryHistEntryList.stream().collect(Collectors.toMap(e -> e.getKey(), e -> e));
}
/**
* @return Returns the processed history map which has interpolated to every hour data.
* The start and end timestamp must be the even hours.
@@ -621,6 +637,24 @@ public final class DataProcessor {
@Nullable
private static UsageEvents getAppUsageEventsForUser(
Context context, final UserManager userManager, final int userID,
final long sixDaysAgoTimestamp) {
final String callingPackage = context.getPackageName();
final long now = System.currentTimeMillis();
// When the user is not unlocked, UsageStatsManager will return null, so bypass the
// following data loading logics directly.
if (!userManager.isUserUnlocked(userID)) {
Log.w(TAG, "fail to load app usage event for user :" + userID + " because locked");
return null;
}
final long startTime = DatabaseUtils.getAppUsageStartTimestampOfUser(
context, userID, sixDaysAgoTimestamp);
return loadAppUsageEventsForUserFromService(
sUsageStatsManager, startTime, now, userID, callingPackage);
}
@Nullable
private static UsageEvents loadAppUsageEventsForUserFromService(
final IUsageStatsManager usageStatsManager, final long startTime, final long endTime,
final int userId, final String callingPackage) {
final long start = System.currentTimeMillis();
@@ -672,14 +706,6 @@ public final class DataProcessor {
return batteryHistEntryList;
}
private static Map<String, BatteryHistEntry> getCurrentBatteryHistoryMapFromStatsService(
final Context context) {
final List<BatteryHistEntry> batteryHistEntryList =
getBatteryHistListFromFromStatsService(context);
return batteryHistEntryList == null ? new HashMap<>()
: batteryHistEntryList.stream().collect(Collectors.toMap(e -> e.getKey(), e -> e));
}
@VisibleForTesting
@Nullable
static List<BatteryHistEntry> convertToBatteryHistEntry(

View File

@@ -194,6 +194,23 @@ public final class DatabaseUtils {
return startCalendar.getTimeInMillis();
}
/** Returns the context with OWNER identity when current user is work profile. */
public static Context getOwnerContext(Context context) {
final boolean isWorkProfileUser = isWorkProfile(context);
if (isWorkProfileUser) {
try {
return context.createPackageContextAsUser(
/*packageName=*/ context.getPackageName(),
/*flags=*/ 0,
/*user=*/ UserHandle.OWNER);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "context.createPackageContextAsUser() fail:" + e);
return null;
}
}
return context;
}
static List<ContentValues> sendAppUsageEventData(
final Context context, final List<AppUsageEvent> appUsageEventList) {
final long startTime = System.currentTimeMillis();
@@ -342,18 +359,9 @@ public final class DatabaseUtils {
private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
Context context, Uri batteryStateUri) {
final boolean isWorkProfileUser = isWorkProfile(context);
Log.d(TAG, "loadHistoryMapFromContentProvider() isWorkProfileUser:" + isWorkProfileUser);
if (isWorkProfileUser) {
try {
context = context.createPackageContextAsUser(
/*packageName=*/ context.getPackageName(),
/*flags=*/ 0,
/*user=*/ UserHandle.OWNER);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "context.createPackageContextAsUser() fail:" + e);
return null;
}
context = DatabaseUtils.getOwnerContext(context);
if (context == null) {
return null;
}
final Map<Long, Map<String, BatteryHistEntry>> resultMap = new HashMap();
try (Cursor cursor = sFakeBatteryStateSupplier != null ? sFakeBatteryStateSupplier.get() :

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.content.Context;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public final class DataProcessManagerTest {
private Context mContext;
private DataProcessManager mDataProcessManager;
@Mock
private IUsageStatsManager mUsageStatsManager;
@Mock
private UserManager mUserManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
DataProcessor.sUsageStatsManager = mUsageStatsManager;
doReturn(mContext).when(mContext).getApplicationContext();
doReturn(mUserManager)
.when(mContext)
.getSystemService(UserManager.class);
mDataProcessManager = new DataProcessManager(
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
/*hourlyBatteryLevelsPerDay=*/ null, /*batteryHistoryMap=*/ null);
}
@Test
public void start_loadExpectedCurrentAppUsageData() throws RemoteException {
final UsageEvents.Event event1 =
getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /*timestamp=*/ 1);
final UsageEvents.Event event2 =
getUsageEvent(UsageEvents.Event.ACTIVITY_STOPPED, /*timestamp=*/ 2);
final List<UsageEvents.Event> events = new ArrayList<>();
events.add(event1);
events.add(event2);
doReturn(getUsageEvents(events))
.when(mUsageStatsManager)
.queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
doReturn(true).when(mUserManager).isUserUnlocked(anyInt());
mDataProcessManager.start();
assertThat(mDataProcessManager.getIsCurrentAppUsageLoaded()).isTrue();
assertThat(mDataProcessManager.getIsDatabaseAppUsageLoaded()).isTrue();
assertThat(mDataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
assertThat(mDataProcessManager.getShowScreenOnTime()).isTrue();
final List<AppUsageEvent> appUsageEventList = mDataProcessManager.getAppUsageEventList();
assertThat(appUsageEventList.size()).isEqualTo(2);
assertAppUsageEvent(
appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1);
assertAppUsageEvent(
appUsageEventList.get(1), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 2);
}
@Test
public void start_currentUserLocked_emptyAppUsageList() throws RemoteException {
final UsageEvents.Event event =
getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /*timestamp=*/ 1);
final List<UsageEvents.Event> events = new ArrayList<>();
events.add(event);
doReturn(getUsageEvents(events))
.when(mUsageStatsManager)
.queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
doReturn(false).when(mUserManager).isUserUnlocked(anyInt());
mDataProcessManager.start();
assertThat(mDataProcessManager.getAppUsageEventList()).isEmpty();
assertThat(mDataProcessManager.getShowScreenOnTime()).isFalse();
}
private UsageEvents getUsageEvents(final List<UsageEvents.Event> events) {
UsageEvents usageEvents = new UsageEvents(events, new String[] {"package"});
Parcel parcel = Parcel.obtain();
parcel.setDataPosition(0);
usageEvents.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
return UsageEvents.CREATOR.createFromParcel(parcel);
}
private UsageEvents.Event getUsageEvent(
final int eventType, final long timestamp) {
final UsageEvents.Event event = new UsageEvents.Event();
event.mEventType = eventType;
event.mPackage = "package";
event.mTimeStamp = timestamp;
return event;
}
private void assertAppUsageEvent(
final AppUsageEvent event, final AppUsageEventType eventType, final long timestamp) {
assertThat(event.getType()).isEqualTo(eventType);
assertThat(event.getTimestamp()).isEqualTo(timestamp);
}
}

View File

@@ -64,7 +64,7 @@ import java.util.Set;
import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class)
public class DataProcessorTest {
public final class DataProcessorTest {
private static final String FAKE_ENTRY_KEY = "fake_entry_key";
private Context mContext;
@@ -177,7 +177,7 @@ public class DataProcessorTest {
}
@Test
public void getAppUsageEvents_lockedUser_returnNull() throws RemoteException {
public void getAppUsageEvents_lockedUser_returnNull() {
UserInfo userInfo = new UserInfo(/*id=*/ 0, "user_0", /*flags=*/ 0);
final List<UserInfo> userInfoList = new ArrayList<>();
userInfoList.add(userInfo);
@@ -205,6 +205,37 @@ public class DataProcessorTest {
assertThat(resultMap).isNull();
}
@Test
public void getAppUsageEventsForUser_returnExpectedResult() throws RemoteException {
final int userId = 1;
doReturn(true).when(mUserManager).isUserUnlocked(userId);
doReturn(mUsageEvents1)
.when(mUsageStatsManager)
.queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId))
.isEqualTo(mUsageEvents1);
}
@Test
public void getAppUsageEventsForUser_lockedUser_returnNull() {
final int userId = 1;
// Test locked user.
doReturn(false).when(mUserManager).isUserUnlocked(userId);
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId)).isNull();
}
@Test
public void getAppUsageEventsForUser_nullUsageEvents_returnNull() throws RemoteException {
final int userId = 1;
doReturn(true).when(mUserManager).isUserUnlocked(userId);
doReturn(null)
.when(mUsageStatsManager).queryEventsForUser(anyLong(), anyLong(), anyInt(), any());
assertThat(DataProcessor.getAppUsageEventsForUser(mContext, userId)).isNull();
}
@Test public void generateAppUsageEventListFromUsageEvents_returnExpectedResult() {
Event event1 = getUsageEvent(Event.NOTIFICATION_INTERRUPTION, /*timestamp=*/ 1);
Event event2 = getUsageEvent(Event.ACTIVITY_RESUMED, /*timestamp=*/ 2);
@@ -231,11 +262,11 @@ public class DataProcessorTest {
DataProcessor.generateAppUsageEventListFromUsageEvents(mContext, appUsageEvents);
assertThat(appUsageEventList.size()).isEqualTo(3);
assetAppUsageEvent(
assertAppUsageEvent(
appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 2);
assetAppUsageEvent(
assertAppUsageEvent(
appUsageEventList.get(1), AppUsageEventType.ACTIVITY_STOPPED, /*timestamp=*/ 3);
assetAppUsageEvent(
assertAppUsageEvent(
appUsageEventList.get(2), AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 4);
}
@@ -1327,7 +1358,7 @@ public class DataProcessorTest {
return event;
}
private void assetAppUsageEvent(
private void assertAppUsageEvent(
final AppUsageEvent event, final AppUsageEventType eventType, final long timestamp) {
assertThat(event.getType()).isEqualTo(eventType);
assertThat(event.getTimestamp()).isEqualTo(timestamp);