Revamp the battery usage details page.

This cl adds AdvancedPowerUsageDetail to show the usage details page.
The AdvancedPowerUsageDetail contains all the needed ui components:
1. App Header
2. Two buttons
3. Usage breakdown preference category
4. Power management preference category

This cl also adds preference controller for two buttons but the
detail implementation will be added in the following cl.

Following cl will also remove previous detail page.

Bug: 35810915
Test: RunSettingsRoboTests
Change-Id: I17f95d1288762094671c0f148fa73367e51f175e
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1631e9c..a271321 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4417,8 +4417,12 @@
 
     <!-- Title for the background activity setting, which allows a user to control whether an app can run in the background [CHAR_LIMIT=40] -->
     <string name="background_activity_title">Background activity</string>
-    <!-- Summary for the background activity [CHAR_LIMIT=120] -->
-    <string name="background_activity_summary">Allow the app to run in the background</string>
+    <!-- Summary for the background activity when it is on [CHAR_LIMIT=120] -->
+    <string name="background_activity_summary_on">App can run in the background when not in use</string>
+    <!-- Summary for the background activity when it is off [CHAR_LIMIT=120] -->
+    <string name="background_activity_summary_off">App\'s background activity is limited when not in use</string>
+    <!-- Summary for the background activity when it is disabled [CHAR_LIMIT=120] -->
+    <string name="background_activity_summary_disabled">App not allowed to run in background</string>
 
     <!-- Title for the screen usage in power use UI [CHAR_LIMIT=40] -->
     <string name="device_screen_usage">Screen usage since full charge</string>
@@ -4632,13 +4636,25 @@
     <!-- Description for battery usage info for an app, i.e. 60% used by facebook. [CHAR LIMIT=60] -->
     <string name="battery_used_by"><xliff:g id="percent">%1$s</xliff:g> used by <xliff:g id="app">%2$s</xliff:g></string>
     <!-- Description for percentage of battery usage for an app, i.e. Screen: 30% of overall battery. [CHAR LIMIT=60] -->
-    <string name="battery_overall_usage">%1$s of overall battery</string>
+    <string name="battery_overall_usage"><xliff:g id="percent">%1$s</xliff:g> of overall battery</string>
     <!-- Description for battery usage detail information since last full charge. [CHAR LIMIT=120] -->
     <string name="battery_detail_since_full_charge">Breakdown since last full charge</string>
     <!-- Title for usage time since last full charge. [CHAR LIMIT=60] -->
     <string name="battery_last_full_charge">Last full charge</string>
     <!-- Description for text in battery footer. [CHAR LIMIT=120] -->
     <string name="battery_footer_summary">Remaining battery time is approximate and can change based on usage</string>
+    <!-- Title for battery usage detail in foreground. [CHAR LIMIT=80] -->
+    <string name="battery_detail_foreground">While using app</string>
+    <!-- Title for battery usage detail in background. [CHAR LIMIT=80] -->
+    <string name="battery_detail_background">While in background</string>
+    <!-- Title for battery usage amount by this app. [CHAR LIMIT=80] -->
+    <string name="battery_detail_power_usage">Battery usage</string>
+    <!-- Description for battery usage amount, i.e. 16% of overall app usage(340 mAh). [CHAR LIMIT=120] -->
+    <string name="battery_detail_power_percentage"><xliff:g id="percent">%1$s</xliff:g> of total app usage (<xliff:g id="power">%2$d</xliff:g>mAh)</string>
+    <!-- Title for the battery usage group, which means all the battery data are calculated 'since full charge' [CHAR LIMIT=40] -->
+    <string name ="battery_detail_info_title">Since full charge</string>
+    <!-- Title for the battery management group [CHAR LIMIT=40] -->
+    <string name ="battery_detail_manage_title">Manage battery usage</string>
 
     <!-- Description for battery time left, i.e. 50min Estimated time left. [CHAR LIMIT=80]-->
     <string name="estimated_time_left">Estimated time left</string>
diff --git a/res/xml/power_usage_detail_ia.xml b/res/xml/power_usage_detail_ia.xml
new file mode 100644
index 0000000..cfaa712
--- /dev/null
+++ b/res/xml/power_usage_detail_ia.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 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.
+  -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="header_view"
+        android:layout="@layout/app_details"
+        android:selectable="false"
+        android:order="-10000"/>
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="action_buttons"
+        android:layout="@layout/app_action_buttons"
+        android:selectable="false"
+        android:order="-9999"/>
+
+    <PreferenceCategory
+        android:title="@string/battery_detail_info_title">
+
+        <Preference
+            android:key="app_usage_foreground"
+            android:title="@string/battery_detail_foreground"/>
+
+        <Preference
+            android:key="app_usage_background"
+            android:title="@string/battery_detail_background"/>
+
+        <Preference
+            android:key="app_power_usage"
+            android:title="@string/battery_detail_power_usage"/>
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:title="@string/battery_detail_manage_title">
+
+        <SwitchPreference
+            android:key="background_activity"
+            android:title="@string/background_activity_title"
+            android:selectable="true"/>
+
+        <Preference
+            android:key="battery_optimization"
+            android:title="@string/battery_detail_background"
+            android:summary="@string/high_power_off"
+            android:selectable="true"/>
+
+    </PreferenceCategory>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 909ddfe..4c8fb2d 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -74,6 +74,7 @@
 import android.provider.ContactsContract.RawContacts;
 import android.provider.Settings;
 import android.service.persistentdata.PersistentDataBlockManager;
+import android.support.annotation.StringRes;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
 import android.support.v7.preference.PreferenceManager;
@@ -1241,6 +1242,17 @@
         return isVolumeValid(volume) ? volume : null;
     }
 
+    /**
+     * Return the resource id to represent the install status for an app
+     */
+    @StringRes
+    public static int getInstallationStatus(ApplicationInfo info) {
+        if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+            return R.string.not_installed;
+        }
+        return info.enabled ? R.string.installed : R.string.disabled;
+    }
+
     private static boolean isVolumeValid(VolumeInfo volume) {
         return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE)
                 && volume.isMountedReadable();
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index e00ba92..dac10e7 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -546,7 +546,7 @@
             .newAppHeaderController(this, appSnippet)
             .setLabel(mAppEntry)
             .setIcon(mAppEntry)
-            .setSummary(getString(getInstallationStatus(mAppEntry.info)))
+            .setSummary(getString(Utils.getInstallationStatus(mAppEntry.info)))
             .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
             .done(false /* rebindActions */);
         mVersionPreference.setSummary(getString(R.string.version_text, pkgInfo.versionName));
@@ -574,14 +574,6 @@
         return showIt;
     }
 
-    @VisibleForTesting
-    int getInstallationStatus(ApplicationInfo info) {
-        if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
-            return R.string.not_installed;
-        }
-        return info.enabled ? R.string.installed : R.string.disabled;
-    }
-
     private boolean signaturesMatch(String pkg1, String pkg2) {
         if (pkg1 != null && pkg2 != null) {
             try {
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
new file mode 100644
index 0000000..ece6b78
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.content.Context;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.Preference;
+import android.view.View;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.internal.util.ArrayUtils;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.Utils;
+import com.android.settings.applications.AppHeaderController;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.applications.ApplicationsState;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Power usage detail fragment for each app, this fragment contains
+ *
+ * 1. Detail battery usage information for app(i.e. usage time, usage amount)
+ * 2. Battery related controls for app(i.e uninstall, force stop)
+ *
+ * This fragment will replace {@link PowerUsageDetail}
+ */
+public class AdvancedPowerUsageDetail extends PowerUsageBase {
+
+    public static final String TAG = "AdvancedPowerUsageDetail";
+    public static final String EXTRA_UID = "extra_uid";
+    public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
+    public static final String EXTRA_FOREGROUND_TIME = "extra_foreground_time";
+    public static final String EXTRA_BACKGROUND_TIME = "extra_background_time";
+    public static final String EXTRA_LABEL = "extra_label";
+    public static final String EXTRA_ICON_ID = "extra_icon_id";
+    public static final String EXTRA_POWER_USAGE_PERCENT = "extra_power_usage_percent";
+    public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount";
+
+    private static final String KEY_PREF_FOREGROUND = "app_usage_foreground";
+    private static final String KEY_PREF_BACKGROUND = "app_usage_background";
+    private static final String KEY_PREF_POWER_USAGE = "app_power_usage";
+    private static final String KEY_PREF_HEADER = "header_view";
+
+    @VisibleForTesting
+    LayoutPreference mHeaderPreference;
+    @VisibleForTesting
+    ApplicationsState mState;
+    @VisibleForTesting
+    ApplicationsState.AppEntry mAppEntry;
+
+    private Preference mForegroundPreference;
+    private Preference mBackgroundPreference;
+    private Preference mPowerUsagePreference;
+
+    public static void startBatteryDetailPage(SettingsActivity caller, PreferenceFragment fragment,
+            BatteryStatsHelper helper, int which, BatteryEntry entry, String usagePercent) {
+        // Initialize mStats if necessary.
+        helper.getStats();
+
+        final Bundle args = new Bundle();
+        final BatterySipper sipper = entry.sipper;
+        final BatteryStats.Uid uid = sipper.uidObj;
+
+        final long backgroundTimeMs = BatteryUtils.getProcessTimeMs(
+                BatteryUtils.StatusType.BACKGROUND, uid, which);
+        final long foregroundTimeMs = BatteryUtils.getProcessTimeMs(
+                BatteryUtils.StatusType.FOREGROUND, uid, which);
+
+        if (ArrayUtils.isEmpty(sipper.mPackages)) {
+            // populate data for system app
+            args.putString(EXTRA_LABEL, entry.getLabel());
+            args.putInt(EXTRA_ICON_ID, entry.iconId);
+            args.putString(EXTRA_PACKAGE_NAME, null);
+        } else {
+            // populate data for normal app
+            args.putString(EXTRA_PACKAGE_NAME, sipper.mPackages[0]);
+        }
+
+        args.putInt(EXTRA_UID, sipper.getUid());
+        args.putLong(EXTRA_BACKGROUND_TIME, backgroundTimeMs);
+        args.putLong(EXTRA_FOREGROUND_TIME, foregroundTimeMs);
+        args.putString(EXTRA_POWER_USAGE_PERCENT, usagePercent);
+        args.putInt(EXTRA_POWER_USAGE_AMOUNT, (int) sipper.totalPowerMah);
+
+        caller.startPreferencePanelAsUser(fragment, AdvancedPowerUsageDetail.class.getName(), args,
+                R.string.details_title, null, new UserHandle(UserHandle.myUserId()));
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mForegroundPreference = findPreference(KEY_PREF_FOREGROUND);
+        mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
+        mPowerUsagePreference = findPreference(KEY_PREF_POWER_USAGE);
+        mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
+        mState = ApplicationsState.getInstance(getActivity().getApplication());
+
+        final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME);
+        if (packageName != null) {
+            mAppEntry = mState.getEntry(packageName, UserHandle.myUserId());
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        initHeader();
+
+        final Bundle bundle = getArguments();
+        final Context context = getContext();
+
+        final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
+        final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
+        final String usagePercent = bundle.getString(EXTRA_POWER_USAGE_PERCENT);
+        final int powerMah = bundle.getInt(EXTRA_POWER_USAGE_AMOUNT);
+        mForegroundPreference.setSummary(Utils.formatElapsedTime(context, foregroundTimeMs, false));
+        mBackgroundPreference.setSummary(Utils.formatElapsedTime(context, backgroundTimeMs, false));
+        mPowerUsagePreference.setSummary(
+                getString(R.string.battery_detail_power_percentage, usagePercent, powerMah));
+    }
+
+    @VisibleForTesting
+    void initHeader() {
+        final View appSnippet = mHeaderPreference.findViewById(R.id.app_snippet);
+        final Context context = getContext();
+        final Bundle bundle = getArguments();
+        AppHeaderController controller = FeatureFactory.getFactory(context)
+                .getApplicationFeatureProvider(context)
+                .newAppHeaderController(this, appSnippet)
+                .setButtonActions(AppHeaderController.ActionType.ACTION_NONE,
+                        AppHeaderController.ActionType.ACTION_NONE);
+
+        if (mAppEntry == null) {
+            controller.setLabel(bundle.getString(EXTRA_LABEL));
+            controller.setIcon(getContext().getDrawable(bundle.getInt(EXTRA_ICON_ID)));
+        } else {
+            mState.ensureIcon(mAppEntry);
+            controller.setLabel(mAppEntry);
+            controller.setIcon(mAppEntry);
+            controller.setSummary(getString(Utils.getInstallationStatus(mAppEntry.info)));
+        }
+
+        controller.done(true /* rebindActions */);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.FUELGAUGE_POWER_USAGE_DETAIL;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.power_usage_detail_ia;
+    }
+
+    @Override
+    protected List<PreferenceController> getPreferenceControllers(Context context) {
+        final List<PreferenceController> controllers = new ArrayList<>();
+        final Bundle bundle = getArguments();
+        final int uid = bundle.getInt(EXTRA_UID, 0);
+        final String packageName = bundle.getString(EXTRA_PACKAGE_NAME);
+
+        controllers.add(new BackgroundActivityPreferenceController(context, uid));
+        controllers.add(new BatteryOptimizationPreferenceController(
+                (SettingsActivity) getActivity(), this));
+        controllers.add(
+                new AppButtonsPreferenceController(getActivity(), getLifecycle(), packageName));
+
+        return controllers;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java b/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java
new file mode 100644
index 0000000..b02c8c5
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/AppButtonsPreferenceController.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.app.Activity;
+import android.os.UserHandle;
+import android.support.v7.preference.PreferenceScreen;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnResume;
+import com.android.settingslib.applications.ApplicationsState;
+
+/**
+ * Controller to control the uninstall button and forcestop button
+ */
+//TODO(b/35810915): refine the button logic and make InstalledAppDetails use this controller
+//TODO(b/35810915): add test for this file
+public class AppButtonsPreferenceController extends PreferenceController implements
+        LifecycleObserver, OnResume {
+    private static final String KEY_ACTION_BUTTONS = "action_buttons";
+
+    private ApplicationsState.AppEntry mAppEntry;
+    private LayoutPreference mButtonsPref;
+    private Button mForceStopButton;
+    private Button mUninstallButton;
+
+    public AppButtonsPreferenceController(Activity activity, Lifecycle lifecycle,
+            String packageName) {
+        super(activity);
+
+        lifecycle.addObserver(this);
+        ApplicationsState state = ApplicationsState.getInstance(activity.getApplication());
+
+        if (packageName != null) {
+            mAppEntry = state.getEntry(packageName, UserHandle.myUserId());
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mAppEntry != null;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        if (isAvailable()) {
+            mButtonsPref = (LayoutPreference) screen.findPreference(KEY_ACTION_BUTTONS);
+
+            mUninstallButton = (Button) mButtonsPref.findViewById(R.id.left_button);
+            mUninstallButton.setText(R.string.uninstall_text);
+
+            mForceStopButton = (Button) mButtonsPref.findViewById(R.id.right_button);
+            mForceStopButton.setText(R.string.force_stop);
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_ACTION_BUTTONS;
+    }
+
+    @Override
+    public void onResume() {
+        //TODO(b/35810915): check and update the status of buttons
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
index 0f08398..c249676 100644
--- a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
@@ -23,6 +23,8 @@
 import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
 import android.util.Log;
+
+import com.android.settings.R;
 import com.android.settings.core.PreferenceController;
 
 /**
@@ -56,8 +58,12 @@
 
         if (mode == AppOpsManager.MODE_ERRORED) {
             preference.setEnabled(false);
+            preference.setSummary(R.string.background_activity_summary_disabled);
         } else {
-            ((SwitchPreference) preference).setChecked(mode != AppOpsManager.MODE_IGNORED);
+            final boolean checked = mode != AppOpsManager.MODE_IGNORED;
+            ((SwitchPreference) preference).setChecked(checked);
+            preference.setSummary(checked ? R.string.background_activity_summary_on
+                    : R.string.background_activity_summary_off);
         }
     }
 
diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizationPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryOptimizationPreferenceController.java
new file mode 100644
index 0000000..946a9b8
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatteryOptimizationPreferenceController.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settings.SettingsActivity;
+import com.android.settings.applications.ManageApplications;
+import com.android.settings.core.PreferenceController;
+
+/**
+ * Controller that jumps to high power optimization fragment
+ */
+public class BatteryOptimizationPreferenceController extends PreferenceController {
+
+    private static final String KEY_BACKGROUND_ACTIVITY = "battery_optimization";
+
+    private Fragment mFragment;
+    private SettingsActivity mSettingsActivity;
+
+    public BatteryOptimizationPreferenceController(SettingsActivity settingsActivity,
+            Fragment fragment) {
+        super(settingsActivity);
+        mFragment = fragment;
+        mSettingsActivity = settingsActivity;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BACKGROUND_ACTIVITY;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (!KEY_BACKGROUND_ACTIVITY.equals(preference.getKey())) {
+            return false;
+        }
+
+        Bundle args = new Bundle(1);
+        args.putString(ManageApplications.EXTRA_CLASSNAME,
+                Settings.HighPowerApplicationsActivity.class.getName());
+        mSettingsActivity.startPreferencePanel(mFragment, ManageApplications.class.getName(), args,
+                R.string.high_power_apps, null, null, 0);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
new file mode 100644
index 0000000..85bc0fd
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.IntDef;
+import android.os.BatteryStats;
+import android.os.SystemClock;
+import android.support.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Utils for battery operation
+ */
+public class BatteryUtils {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({StatusType.FOREGROUND,
+            StatusType.BACKGROUND,
+            StatusType.ALL
+    })
+    public @interface StatusType {
+        int FOREGROUND = 0;
+        int BACKGROUND = 1;
+        int ALL = 2;
+    }
+
+    public static long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid,
+            int which) {
+        if (uid == null) {
+            return 0;
+        }
+
+        switch (type) {
+            case StatusType.FOREGROUND:
+                return getProcessForegroundTimeMs(uid, which);
+            case StatusType.BACKGROUND:
+                return getProcessBackgroundTimeMs(uid, which);
+            case StatusType.ALL:
+                return getProcessForegroundTimeMs(uid, which)
+                        + getProcessBackgroundTimeMs(uid, which);
+        }
+        return 0;
+    }
+
+    private static long getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which) {
+        final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
+        final long timeUs = uid.getProcessStateTime(
+                BatteryStats.Uid.PROCESS_STATE_BACKGROUND, rawRealTimeUs, which);
+        return  convertUsToMs(timeUs);
+    }
+
+    private static long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) {
+        final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
+        final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP,
+                BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE,
+                BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING,
+                BatteryStats.Uid.PROCESS_STATE_FOREGROUND};
+        long timeUs = 0;
+        for (int type : foregroundTypes) {
+            timeUs += uid.getProcessStateTime(type, rawRealTimeUs, which);
+        }
+
+        return convertUsToMs(timeUs);
+    }
+
+    private static long convertUsToMs(long timeUs) {
+        return timeUs / 1000;
+    }
+
+    private static long convertMsToUs(long timeMs) {
+        return timeMs * 1000;
+    }
+
+}
+
diff --git a/src/com/android/settings/fuelgauge/PowerGaugePreference.java b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
index fe7ef6e..d4f2dd2 100644
--- a/src/com/android/settings/fuelgauge/PowerGaugePreference.java
+++ b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
@@ -63,6 +63,10 @@
         notifyChanged();
     }
 
+    public String getPercent() {
+        return mProgress.toString();
+    }
+
     BatteryEntry getInfo() {
         return mInfo;
     }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java
index 956b279..f0d0a10 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageDetail.java
@@ -69,6 +69,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+// TODO(b/35810915): Delete this page once ag/1971493 is done.
 public class PowerUsageDetail extends PowerUsageBase implements Button.OnClickListener {
 
     // Note: Must match the sequence of the DrainType
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 1ffc594..b0e8fb0 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -165,8 +165,8 @@
         }
         PowerGaugePreference pgp = (PowerGaugePreference) preference;
         BatteryEntry entry = pgp.getInfo();
-        PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(), this,
-                mStatsHelper, mStatsType, entry, true, true);
+        AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
+                this, mStatsHelper, mStatsType, entry, pgp.getPercent());
         return super.onPreferenceTreeClick(preference);
     }
 
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index 778440d..240b8eb 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -4,5 +4,6 @@
 com.android.settings.inputmethod.InputAndGestureSettings
 com.android.settings.accounts.AccountDetailDashboardFragment
 com.android.settings.fuelgauge.PowerUsageDetail
+com.android.settings.fuelgauge.AdvancedPowerUsageDetail
 com.android.settings.deviceinfo.StorageProfileFragment
 com.android.settings.wifi.details.WifiNetworkDetailsFragment
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index 324e751..8b7605c 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -9,6 +9,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.net.ConnectivityManager;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -116,4 +117,28 @@
 
         Utils.maybeInitializeVolume(storageManager, new Bundle());
     }
+
+    @Test
+    public void getInstallationStatus_notInstalled_shouldReturnUninstalled() {
+        assertThat(Utils.getInstallationStatus(new ApplicationInfo()))
+                .isEqualTo(R.string.not_installed);
+    }
+
+    @Test
+    public void getInstallationStatus_enabled_shouldReturnInstalled() {
+        final ApplicationInfo info = new ApplicationInfo();
+        info.flags = ApplicationInfo.FLAG_INSTALLED;
+        info.enabled = true;
+
+        assertThat(Utils.getInstallationStatus(info)).isEqualTo(R.string.installed);
+    }
+
+    @Test
+    public void getInstallationStatus_disabled_shouldReturnDisabled() {
+        final ApplicationInfo info = new ApplicationInfo();
+        info.flags = ApplicationInfo.FLAG_INSTALLED;
+        info.enabled = false;
+
+        assertThat(Utils.getInstallationStatus(info)).isEqualTo(R.string.disabled);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
index b0cd8d5..bf00889 100644
--- a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
@@ -68,31 +68,6 @@
     }
 
     @Test
-    public void getInstallationStatus_notInstalled_shouldReturnUninstalled() {
-
-        assertThat(mAppDetail.getInstallationStatus(new ApplicationInfo()))
-                .isEqualTo(R.string.not_installed);
-    }
-
-    @Test
-    public void getInstallationStatus_enabled_shouldReturnInstalled() {
-        final ApplicationInfo info = new ApplicationInfo();
-        info.flags = ApplicationInfo.FLAG_INSTALLED;
-        info.enabled = true;
-
-        assertThat(mAppDetail.getInstallationStatus(info)).isEqualTo(R.string.installed);
-    }
-
-    @Test
-    public void getInstallationStatus_disabled_shouldReturnDisabled() {
-        final ApplicationInfo info = new ApplicationInfo();
-        info.flags = ApplicationInfo.FLAG_INSTALLED;
-        info.enabled = false;
-
-        assertThat(mAppDetail.getInstallationStatus(info)).isEqualTo(R.string.disabled);
-    }
-
-    @Test
     public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() {
         when(mDevicePolicyManager.packageHasActiveAdmins(anyString())).thenReturn(false);
         when(mUserManager.getUsers().size()).thenReturn(2);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
new file mode 100644
index 0000000..8b5ce19
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.graphics.drawable.Drawable;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.view.View;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.SettingsActivity;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.AppHeaderController;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settingslib.applications.ApplicationsState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class AdvancedPowerUsageDetailTest {
+    private static final String APP_LABEL = "app label";
+    private static final String SUMMARY = "summary";
+    private static final String[] PACKAGE_NAME = {"com.android.app"};
+    private static final String USAGE_PERCENT = "16";
+    private static final int ICON_ID = 123;
+    private static final int UID = 1;
+    private static final long BACKGROUND_TIME_US = 100 * 1000;
+    private static final long FOREGROUND_TIME_US = 200 * 1000;
+    private static final long BACKGROUND_TIME_MS = 100;
+    private static final long FOREGROUND_TIME_MS = 200;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private AppHeaderController mAppHeaderController;
+    @Mock
+    private LayoutPreference mHeaderPreference;
+    @Mock
+    private ApplicationsState mState;
+    @Mock
+    private ApplicationsState.AppEntry mAppEntry;
+    @Mock
+    private Drawable mIconDrawable;
+    @Mock
+    private Bundle mBundle;
+    @Mock
+    private BatteryEntry mBatteryEntry;
+    @Mock
+    private BatterySipper mBatterySipper;
+    @Mock
+    private BatteryStatsHelper mBatteryStatsHelper;
+    @Mock
+    private BatteryStats.Uid mUid;
+    private Bundle mTestBundle;
+    private AdvancedPowerUsageDetail mFragment;
+    private FakeFeatureFactory mFeatureFactory;
+    private SettingsActivity mTestActivity;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        FakeFeatureFactory.setupForTest(mContext);
+        mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+
+        mFragment = spy(new AdvancedPowerUsageDetail());
+        doReturn(mContext).when(mFragment).getContext();
+        doReturn(SUMMARY).when(mFragment).getString(anyInt());
+        doReturn(APP_LABEL).when(mBundle).getString(anyString());
+        doReturn(mBundle).when(mFragment).getArguments();
+
+        doReturn(mAppHeaderController).when(mFeatureFactory.applicationFeatureProvider)
+                .newAppHeaderController(any(Fragment.class), any(View.class));
+        doReturn(mAppHeaderController).when(mAppHeaderController).setButtonActions(anyInt(),
+                anyInt());
+        doReturn(mAppHeaderController).when(mAppHeaderController).setIcon(any(Drawable.class));
+        doReturn(mAppHeaderController).when(mAppHeaderController).setIcon(any(
+                ApplicationsState.AppEntry.class));
+        doReturn(mAppHeaderController).when(mAppHeaderController).setLabel(anyString());
+        doReturn(mAppHeaderController).when(mAppHeaderController).setLabel(any(
+                ApplicationsState.AppEntry.class));
+        doReturn(mAppHeaderController).when(mAppHeaderController).setSummary(anyString());
+
+
+        doReturn(UID).when(mBatterySipper).getUid();
+        doReturn(APP_LABEL).when(mBatteryEntry).getLabel();
+        doReturn(BACKGROUND_TIME_US).when(mUid).getProcessStateTime(
+                eq(BatteryStats.Uid.PROCESS_STATE_BACKGROUND), anyLong(), anyInt());
+        doReturn(FOREGROUND_TIME_US).when(mUid).getProcessStateTime(
+                eq(BatteryStats.Uid.PROCESS_STATE_FOREGROUND), anyLong(), anyInt());
+        ReflectionHelpers.setField(mBatteryEntry, "sipper", mBatterySipper);
+        mBatteryEntry.iconId = ICON_ID;
+        mBatterySipper.uidObj = mUid;
+
+        mFragment.mHeaderPreference = mHeaderPreference;
+        mFragment.mState = mState;
+        mAppEntry.info = mock(ApplicationInfo.class);
+
+        mTestActivity = spy(new SettingsActivity());
+
+        final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
+
+        Answer<Void> callable = new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Exception {
+                mBundle = captor.getValue();
+                return null;
+            }
+        };
+        doAnswer(callable).when(mTestActivity).startPreferencePanelAsUser(any(), anyString(),
+                captor.capture(), anyInt(), any(), any());
+    }
+
+    @Test
+    public void testInitHeader_NoAppEntry_BuildByBundle() {
+        mFragment.mAppEntry = null;
+        mFragment.initHeader();
+
+        verify(mAppHeaderController).setIcon(any(Drawable.class));
+        verify(mAppHeaderController).setLabel(APP_LABEL);
+    }
+
+    @Test
+    public void testInitHeader_HasAppEntry_BuildByAppEntry() {
+        mFragment.mAppEntry = mAppEntry;
+        mFragment.initHeader();
+
+        verify(mAppHeaderController).setIcon(mAppEntry);
+        verify(mAppHeaderController).setLabel(mAppEntry);
+    }
+
+    @Test
+    public void testStartBatteryDetailPage_hasBasicData() {
+        AdvancedPowerUsageDetail.startBatteryDetailPage(mTestActivity, null, mBatteryStatsHelper, 0,
+                mBatteryEntry, USAGE_PERCENT);
+
+        assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID);
+        assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME)).isEqualTo(
+                BACKGROUND_TIME_MS);
+        assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME)).isEqualTo(
+                FOREGROUND_TIME_MS);
+        assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT)).isEqualTo(
+                USAGE_PERCENT);
+    }
+
+    @Test
+    public void testStartBatteryDetailPage_NormalApp() {
+        mBatterySipper.mPackages = PACKAGE_NAME;
+        AdvancedPowerUsageDetail.startBatteryDetailPage(mTestActivity, null, mBatteryStatsHelper, 0,
+                mBatteryEntry, USAGE_PERCENT);
+
+        assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME)).isEqualTo(
+                PACKAGE_NAME[0]);
+    }
+
+    @Test
+    public void testStartBatteryDetailPage_SystemApp() {
+        mBatterySipper.mPackages = null;
+        AdvancedPowerUsageDetail.startBatteryDetailPage(mTestActivity, null, mBatteryStatsHelper, 0,
+                mBatteryEntry, USAGE_PERCENT);
+
+        assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_LABEL)).isEqualTo(APP_LABEL);
+        assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_ICON_ID)).isEqualTo(ICON_ID);
+        assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME)).isEqualTo(null);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationPreferenceControllerTest.java
new file mode 100644
index 0000000..2c5296a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationPreferenceControllerTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.SettingsActivity;
+import com.android.settings.TestConfig;
+
+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.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BatteryOptimizationPreferenceControllerTest {
+    private static final String KEY_OPTIMIZATION = "battery_optimization";
+    private static final String KEY_OTHER = "other";
+    @Mock
+    private SettingsActivity mSettingsActivity;
+    @Mock
+    private Fragment mFragment;
+    @Mock
+    private Preference mPreference;
+
+    private BatteryOptimizationPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mController = new BatteryOptimizationPreferenceController(mSettingsActivity, mFragment);
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick_OptimizationPreference_HandleClick() {
+        when(mPreference.getKey()).thenReturn(KEY_OPTIMIZATION);
+
+        final boolean handled = mController.handlePreferenceTreeClick(mPreference);
+
+        assertThat(handled).isTrue();
+        verify(mSettingsActivity).startPreferencePanel(any(Fragment.class),
+                anyString(), any(Bundle.class), anyInt(), any(CharSequence.class),
+                any(Fragment.class), anyInt());
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick_OtherPreference_NotHandleClick() {
+        when(mPreference.getKey()).thenReturn(KEY_OTHER);
+
+        final boolean handled = mController.handlePreferenceTreeClick(mPreference);
+
+        assertThat(handled).isFalse();
+        verify(mSettingsActivity, never()).startPreferencePanel(any(Fragment.class),
+                anyString(), any(Bundle.class), anyInt(), any(CharSequence.class),
+                any(Fragment.class), anyInt());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
new file mode 100644
index 0000000..672cc90
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.os.BatteryStats;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import static android.os.BatteryStats.Uid.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
+import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Matchers.eq;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BatteryUtilsTest {
+    // unit that used to converted ms to us
+    private static final long UNIT = 1000;
+    private static final long TIME_STATE_TOP = 1500 * UNIT;
+    private static final long TIME_STATE_FOREGROUND_SERVICE = 2000 * UNIT;
+    private static final long TIME_STATE_TOP_SLEEPING = 2500 * UNIT;
+    private static final long TIME_STATE_FOREGROUND = 3000 * UNIT;
+    private static final long TIME_STATE_BACKGROUND = 6000 * UNIT;
+
+    private static final long TIME_EXPECTED_FOREGROUND = 9000;
+    private static final long TIME_EXPECTED_BACKGROUND = 6000;
+    private static final long TIME_EXPECTED_ALL = 15000;
+
+    @Mock
+    BatteryStats.Uid mUid;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn(TIME_STATE_TOP).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP), anyLong(),
+                anyInt());
+        doReturn(TIME_STATE_FOREGROUND_SERVICE).when(mUid).getProcessStateTime(
+                eq(PROCESS_STATE_FOREGROUND_SERVICE), anyLong(), anyInt());
+        doReturn(TIME_STATE_TOP_SLEEPING).when(mUid).getProcessStateTime(
+                eq(PROCESS_STATE_TOP_SLEEPING), anyLong(), anyInt());
+        doReturn(TIME_STATE_FOREGROUND).when(mUid).getProcessStateTime(eq(PROCESS_STATE_FOREGROUND),
+                anyLong(), anyInt());
+        doReturn(TIME_STATE_BACKGROUND).when(mUid).getProcessStateTime(eq(PROCESS_STATE_BACKGROUND),
+                anyLong(), anyInt());
+    }
+
+    @Test
+    public void testGetProcessTimeMs_typeForeground_timeCorrect() {
+        final long time = BatteryUtils.getProcessTimeMs(BatteryUtils.StatusType.FOREGROUND, mUid,
+                BatteryStats.STATS_SINCE_CHARGED);
+
+        assertThat(time).isEqualTo(TIME_EXPECTED_FOREGROUND);
+    }
+
+    @Test
+    public void testGetProcessTimeMs_typeBackground_timeCorrect() {
+        final long time = BatteryUtils.getProcessTimeMs(BatteryUtils.StatusType.BACKGROUND, mUid,
+                BatteryStats.STATS_SINCE_CHARGED);
+
+        assertThat(time).isEqualTo(TIME_EXPECTED_BACKGROUND);
+    }
+
+    @Test
+    public void testGetProcessTimeMs_typeAll_timeCorrect() {
+        final long time = BatteryUtils.getProcessTimeMs(BatteryUtils.StatusType.ALL, mUid,
+                BatteryStats.STATS_SINCE_CHARGED);
+
+        assertThat(time).isEqualTo(TIME_EXPECTED_ALL);
+    }
+
+    @Test
+    public void testGetProcessTimeMs_uidNull_returnZero() {
+        final long time = BatteryUtils.getProcessTimeMs(BatteryUtils.StatusType.ALL, null,
+                BatteryStats.STATS_SINCE_CHARGED);
+
+        assertThat(time).isEqualTo(0);
+    }
+}