Add DataProcessManager to manage the async tasks of battery usage data
processing.

Test: make RunSettingsRoboTests + manually
Bug: 260964903
Change-Id: Id3b2772a98ec2ab3b03910c8a5e81adf7ccd5646
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);