Merge "Add DataProcessManager to manage the async tasks of battery usage data processing."
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
new file mode 100644
index 0000000..350bbce
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -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;
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index 2db6849..bc5c031 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -89,9 +89,6 @@
@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 @@
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 @@
@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 @@
}
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);
}
@@ -244,6 +225,30 @@
}
/**
+ * 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.
*/
public static void closeBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
@@ -336,6 +341,17 @@
}
/**
+ * @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.
* The keys of processed history map should contain every hour between the start and end
@@ -621,6 +637,24 @@
@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 @@
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(
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index d7c98a7..f6b4e03 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -194,6 +194,23 @@
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 @@
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() :
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
new file mode 100644
index 0000000..a3578cb
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
@@ -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);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
index b1695eb..f0412df 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
@@ -64,7 +64,7 @@
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 @@
}
@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 @@
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 @@
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 @@
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);