Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6cee8fab authored by Lei Yu's avatar Lei Yu Committed by android-build-merger
Browse files

Merge "Copy smearing method to BatteryStatsHelper" into oc-dr1-dev am: 02ece2f4

am: 35aa7879

Change-Id: Ia67b467eee2cea695914d8d021a172c0059961db
parents 3116d5dd 35aa7879
Loading
Loading
Loading
Loading
+48 −4
Original line number Diff line number Diff line
@@ -17,15 +17,51 @@ package com.android.internal.os;

import android.os.BatteryStats.Uid;

import java.util.List;

/**
 * Contains power usage of an application, system service, or hardware type.
 */
public class BatterySipper implements Comparable<BatterySipper> {
    public int userId;
    public Uid uidObj;
    public double totalPowerMah;
    public DrainType drainType;

    /**
     * Smeared power from screen usage.
     * We split the screen usage power and smear them among apps, based on activity time.
     */
    public double screenPowerMah;

    /**
     * Smeared power using proportional method.
     *
     * we smear power usage from hidden sippers to all apps proportionally.(except for screen usage)
     *
     * @see BatteryStatsHelper#shouldHideSipper(BatterySipper)
     * @see BatteryStatsHelper#removeHiddenBatterySippers(List)
     */
    public double proportionalSmearMah;

    /**
     * Total power that adding the smeared power.
     *
     * @see #sumPower()
     */
    public double totalSmearedPowerMah;

    /**
     * Total power before smearing
     */
    public double totalPowerMah;

    /**
     * Whether we should hide this sipper
     *
     * @see BatteryStatsHelper#shouldHideSipper(BatterySipper)
     */
    public boolean shouldHide;

    /**
     * Generic usage time in milliseconds.
     */
@@ -169,15 +205,23 @@ public class BatterySipper implements Comparable<BatterySipper> {
        cameraPowerMah += other.cameraPowerMah;
        flashlightPowerMah += other.flashlightPowerMah;
        bluetoothPowerMah += other.bluetoothPowerMah;
        screenPowerMah += other.screenPowerMah;
        proportionalSmearMah += other.proportionalSmearMah;
        totalSmearedPowerMah += other.totalSmearedPowerMah;
    }

    /**
     * Sum all the powers and store the value into `value`.
     * Also sum the {@code smearedTotalPowerMah} by adding smeared powerMah.
     *
     * @return the sum of all the power in this BatterySipper.
     */
    public double sumPower() {
        return totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
        totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
                sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
                flashlightPowerMah + bluetoothPowerMah;
        totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah;

        return totalPowerMah;
    }
}
+236 −40
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.internal.os;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.SensorManager;
import android.net.ConnectivityManager;
import android.os.BatteryStats;
@@ -32,12 +34,16 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseLongArray;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.util.ArrayUtils;

import java.io.File;
import java.io.FileInputStream;
@@ -55,7 +61,7 @@ import java.util.Locale;
 * The caller must initialize this class as soon as activity object is ready to use (for example, in
 * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
 */
public final class BatteryStatsHelper {
public class BatteryStatsHelper {
    static final boolean DEBUG = false;

    private static final String TAG = BatteryStatsHelper.class.getSimpleName();
@@ -73,6 +79,10 @@ public final class BatteryStatsHelper {
    private Intent mBatteryBroadcast;
    private PowerProfile mPowerProfile;

    private String[] mSystemPackageArray;
    private String[] mServicepackageArray;
    private PackageManager mPackageManager;

    /**
     * List of apps using power.
     */
@@ -163,6 +173,13 @@ public final class BatteryStatsHelper {
        mContext = context;
        mCollectBatteryBroadcast = collectBatteryBroadcast;
        mWifiOnly = wifiOnly;
        mPackageManager = context.getPackageManager();

        final Resources resources = context.getResources();
        mSystemPackageArray = resources.getStringArray(
                com.android.internal.R.array.config_batteryPackageTypeSystem);
        mServicepackageArray = resources.getStringArray(
                com.android.internal.R.array.config_batteryPackageTypeService);
    }

    public void storeStatsHistoryInFile(String fname) {
@@ -274,15 +291,25 @@ public final class BatteryStatsHelper {
        if (power == 0) return "0";

        final String format;
        if (power < .00001) format = "%.8f";
        else if (power < .0001) format = "%.7f";
        else if (power < .001) format = "%.6f";
        else if (power < .01) format = "%.5f";
        else if (power < .1) format = "%.4f";
        else if (power < 1) format = "%.3f";
        else if (power < 10) format = "%.2f";
        else if (power < 100) format = "%.1f";
        else format = "%.0f";
        if (power < .00001) {
            format = "%.8f";
        } else if (power < .0001) {
            format = "%.7f";
        } else if (power < .001) {
            format = "%.6f";
        } else if (power < .01) {
            format = "%.5f";
        } else if (power < .1) {
            format = "%.4f";
        } else if (power < 1) {
            format = "%.3f";
        } else if (power < 10) {
            format = "%.2f";
        } else if (power < 100) {
            format = "%.1f";
        } else {
            format = "%.0f";
        }

        // Use English locale because this is never used in UI (only in checkin and dump).
        return String.format(Locale.ENGLISH, format, power);
@@ -491,6 +518,21 @@ public final class BatteryStatsHelper {
                mMaxPower = Math.max(mMaxPower, amount);
            }
        }

        // Smear it!
        final double hiddenPowerMah = removeHiddenBatterySippers(mUsageList);
        final double totalRemainingPower = getTotalPower() - hiddenPowerMah;
        if (Math.abs(totalRemainingPower) > 1e-3) {
            for (int i = 0, size = mUsageList.size(); i < size; i++) {
                final BatterySipper sipper = mUsageList.get(i);
                if (!sipper.shouldHide) {
                    sipper.proportionalSmearMah = hiddenPowerMah
                            * ((sipper.totalPowerMah + sipper.screenPowerMah)
                            / totalRemainingPower);
                    sipper.sumPower();
                }
            }
        }
    }

    private void processAppUsage(SparseArray<UserHandle> asUsers) {
@@ -506,12 +548,15 @@ public final class BatteryStatsHelper {

            mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
                    mStatsType);
            mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
                    mStatsType);
            mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
                    mStatsType);

            final double totalPower = app.sumPower();
            if (DEBUG && totalPower != 0) {
@@ -647,7 +692,8 @@ public final class BatteryStatsHelper {
     */
    private void addWiFiUsage() {
        BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
        mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs, mStatsType);
        mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs,
                mStatsType);
        aggregateSippers(bs, mWifiSippers, "WIFI");
        if (bs.totalPowerMah > 0) {
            mUsageList.add(bs);
@@ -719,17 +765,29 @@ public final class BatteryStatsHelper {
        return mMobilemsppList;
    }

    public long getStatsPeriod() { return mStatsPeriod; }
    public long getStatsPeriod() {
        return mStatsPeriod;
    }

    public int getStatsType() { return mStatsType; }
    public int getStatsType() {
        return mStatsType;
    }

    public double getMaxPower() { return mMaxPower; }
    public double getMaxPower() {
        return mMaxPower;
    }

    public double getMaxRealPower() { return mMaxRealPower; }
    public double getMaxRealPower() {
        return mMaxRealPower;
    }

    public double getTotalPower() { return mTotalPower; }
    public double getTotalPower() {
        return mTotalPower;
    }

    public double getComputedPower() { return mComputedPower; }
    public double getComputedPower() {
        return mComputedPower;
    }

    public double getMinDrainedPower() {
        return mMinDrainedPower;
@@ -765,6 +823,144 @@ public final class BatteryStatsHelper {
        }
    }

    /**
     * Mark the {@link BatterySipper} that we should hide and smear the screen usage based on
     * foreground activity time.
     *
     * @param sippers sipper list that need to check and remove
     * @return the total power of the hidden items of {@link BatterySipper}
     * for proportional smearing
     */
    public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
        double proportionalSmearPowerMah = 0;
        BatterySipper screenSipper = null;
        for (int i = sippers.size() - 1; i >= 0; i--) {
            final BatterySipper sipper = sippers.get(i);
            sipper.shouldHide = shouldHideSipper(sipper);
            if (sipper.shouldHide) {
                if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED
                        && sipper.drainType != BatterySipper.DrainType.SCREEN
                        && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED) {
                    // Don't add it if it is overcounted, unaccounted or screen
                    proportionalSmearPowerMah += sipper.totalPowerMah;
                }
            }

            if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
                screenSipper = sipper;
            }
        }

        smearScreenBatterySipper(sippers, screenSipper);

        return proportionalSmearPowerMah;
    }

    /**
     * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
     * time.
     */
    public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
        final long rawRealtimeMs = SystemClock.elapsedRealtime();
        long totalActivityTimeMs = 0;
        final SparseLongArray activityTimeArray = new SparseLongArray();
        for (int i = 0, size = sippers.size(); i < size; i++) {
            final BatteryStats.Uid uid = sippers.get(i).uidObj;
            if (uid != null) {
                final long timeMs = getForegroundActivityTotalTimeMs(uid, rawRealtimeMs);
                activityTimeArray.put(uid.getUid(), timeMs);
                totalActivityTimeMs += timeMs;
            }
        }

        if (totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
            final double screenPowerMah = screenSipper.totalPowerMah;
            for (int i = 0, size = sippers.size(); i < size; i++) {
                final BatterySipper sipper = sippers.get(i);
                sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
                        / totalActivityTimeMs;
            }
        }
    }

    /**
     * Check whether we should hide the battery sipper.
     */
    public boolean shouldHideSipper(BatterySipper sipper) {
        final BatterySipper.DrainType drainType = sipper.drainType;

        return drainType == BatterySipper.DrainType.IDLE
                || drainType == BatterySipper.DrainType.CELL
                || drainType == BatterySipper.DrainType.SCREEN
                || drainType == BatterySipper.DrainType.UNACCOUNTED
                || drainType == BatterySipper.DrainType.OVERCOUNTED
                || isTypeService(sipper)
                || isTypeSystem(sipper);
    }

    /**
     * Check whether {@code sipper} is type service
     */
    public boolean isTypeService(BatterySipper sipper) {
        final String[] packages = mPackageManager.getPackagesForUid(sipper.getUid());
        if (packages == null) {
            return false;
        }

        for (String packageName : packages) {
            if (ArrayUtils.contains(mServicepackageArray, packageName)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check whether {@code sipper} is type system
     */
    public boolean isTypeSystem(BatterySipper sipper) {
        final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
        sipper.mPackages = mPackageManager.getPackagesForUid(uid);
        // Classify all the sippers to type system if the range of uid is 0...FIRST_APPLICATION_UID
        if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
            return true;
        } else if (sipper.mPackages != null) {
            for (final String packageName : sipper.mPackages) {
                if (ArrayUtils.contains(mSystemPackageArray, packageName)) {
                    return true;
                }
            }
        }

        return false;
    }

    @VisibleForTesting
    public long getForegroundActivityTotalTimeMs(BatteryStats.Uid uid, long rawRealtimeMs) {
        final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
        if (timer != null) {
            return timer.getTotalTimeLocked(rawRealtimeMs, BatteryStats.STATS_SINCE_CHARGED);
        }

        return 0;
    }

    @VisibleForTesting
    public void setPackageManager(PackageManager packageManager) {
        mPackageManager = packageManager;
    }

    @VisibleForTesting
    public void setSystemPackageArray(String[] array) {
        mSystemPackageArray = array;
    }

    @VisibleForTesting
    public void setServicePackageArray(String[] array) {
        mServicepackageArray = array;
    }

    private void load() {
        if (mBatteryInfo == null) {
            return;
+10 −0
Original line number Diff line number Diff line
@@ -2968,4 +2968,14 @@

    <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
    <string name="config_headlineFontFamily" translatable="false"></string>

    <!-- An array of packages that need to be treated as type system in battery settings -->
    <string-array translatable="false" name="config_batteryPackageTypeSystem">
        <item>com.android.providers.calendar</item>
        <item>com.android.providers.media</item>
        <item>com.android.systemui</item>
    </string-array>

    <!-- An array of packages that need to be treated as type service in battery settings -->
    <string-array translatable="false" name="config_batteryPackageTypeService"/>
</resources>
+4 −0
Original line number Diff line number Diff line
@@ -3043,4 +3043,8 @@
  <java-symbol type="string" name="config_headlineFontFamily" />

  <java-symbol type="drawable" name="stat_sys_vitals" />

  <java-symbol type="array" name="config_batteryPackageTypeSystem" />
  <java-symbol type="array" name="config_batteryPackageTypeService" />

</resources>
+240 −0
Original line number Diff line number Diff line
/*
 * 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.internal.os;


import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.BatteryStats;
import android.os.Process;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.format.DateUtils;

import junit.framework.TestCase;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.List;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class BatteryStatsHelperTest extends TestCase {
    private static final long TIME_FOREGROUND_ACTIVITY_ZERO = 0;
    private static final long TIME_FOREGROUND_ACTIVITY = 100 * DateUtils.MINUTE_IN_MILLIS;

    private static final int UID = 123456;
    private static final double BATTERY_SCREEN_USAGE = 300;
    private static final double BATTERY_SYSTEM_USAGE = 600;
    private static final double BATTERY_OVERACCOUNTED_USAGE = 500;
    private static final double BATTERY_UNACCOUNTED_USAGE = 700;
    private static final double BATTERY_APP_USAGE = 100;
    private static final double TOTAL_BATTERY_USAGE = 1000;
    private static final double PRECISION = 0.001;

    @Mock
    private BatteryStats.Uid mUid;
    @Mock
    private BatterySipper mNormalBatterySipper;
    @Mock
    private BatterySipper mScreenBatterySipper;
    @Mock
    private BatterySipper mOvercountedBatterySipper;
    @Mock
    private BatterySipper mUnaccountedBatterySipper;
    @Mock
    private BatterySipper mSystemBatterySipper;
    @Mock
    private BatterySipper mCellBatterySipper;
    @Mock
    private PackageManager mPackageManager;

    private BatteryStatsHelper mBatteryStatsHelper;
    private Context mContext;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
        mNormalBatterySipper.totalPowerMah = TOTAL_BATTERY_USAGE;
        when(mNormalBatterySipper.getUid()).thenReturn(UID);
        mNormalBatterySipper.uidObj = mUid;


        mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
        mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE;

        mSystemBatterySipper.drainType = BatterySipper.DrainType.APP;
        mSystemBatterySipper.totalPowerMah = BATTERY_SYSTEM_USAGE;
        mSystemBatterySipper.uidObj = mUid;
        when(mSystemBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID);

        mOvercountedBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
        mOvercountedBatterySipper.totalPowerMah = BATTERY_OVERACCOUNTED_USAGE;

        mUnaccountedBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
        mUnaccountedBatterySipper.totalPowerMah = BATTERY_UNACCOUNTED_USAGE;

        mContext = InstrumentationRegistry.getContext();
        mBatteryStatsHelper = spy(new BatteryStatsHelper(mContext));
        mBatteryStatsHelper.setPackageManager(mPackageManager);
    }

    @Test
    public void testShouldHideSipper_TypeUnAccounted_ReturnTrue() {
        mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
        assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
    }

    @Test
    public void testShouldHideSipper_TypeOverAccounted_ReturnTrue() {
        mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
        assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
    }

    @Test
    public void testShouldHideSipper_TypeIdle_ReturnTrue() {
        mNormalBatterySipper.drainType = BatterySipper.DrainType.IDLE;
        assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
    }

    @Test
    public void testShouldHideSipper_TypeCell_ReturnTrue() {
        mNormalBatterySipper.drainType = BatterySipper.DrainType.CELL;
        assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
    }

    @Test
    public void testShouldHideSipper_TypeScreen_ReturnTrue() {
        mNormalBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
        assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
    }

    @Test
    public void testShouldHideSipper_TypeSystem_ReturnTrue() {
        mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
        when(mNormalBatterySipper.getUid()).thenReturn(Process.ROOT_UID);
        assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
    }

    @Test
    public void testShouldHideSipper_UidNormal_ReturnFalse() {
        mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
        assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isFalse();
    }

    @Test
    public void testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue() {
        final List<BatterySipper> sippers = new ArrayList<>();
        sippers.add(mNormalBatterySipper);
        sippers.add(mScreenBatterySipper);
        sippers.add(mSystemBatterySipper);
        sippers.add(mOvercountedBatterySipper);
        sippers.add(mUnaccountedBatterySipper);
        doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper);
        doNothing().when(mBatteryStatsHelper).smearScreenBatterySipper(any(), any());

        final double totalUsage = mBatteryStatsHelper.removeHiddenBatterySippers(sippers);

        assertThat(mNormalBatterySipper.shouldHide).isFalse();
        assertThat(mScreenBatterySipper.shouldHide).isTrue();
        assertThat(mSystemBatterySipper.shouldHide).isTrue();
        assertThat(mOvercountedBatterySipper.shouldHide).isTrue();
        assertThat(mUnaccountedBatterySipper.shouldHide).isTrue();
        assertThat(totalUsage).isWithin(PRECISION).of(BATTERY_SYSTEM_USAGE);
    }

    @Test
    public void testSmearScreenBatterySipper() {
        final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
                BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */);
        final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
                BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */);
        final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY,
                BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */);

        final List<BatterySipper> sippers = new ArrayList<>();
        sippers.add(sipperNull);
        sippers.add(sipperBg);
        sippers.add(sipperFg);

        mBatteryStatsHelper.smearScreenBatterySipper(sippers, mScreenBatterySipper);

        assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0);
        assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0);
        assertThat(sipperFg.screenPowerMah).isWithin(PRECISION).of(BATTERY_SCREEN_USAGE);
    }

    @Test
    public void testIsTypeSystem_systemPackage_returnTrue() {
        final String[] systemPackages = {"com.android.system"};
        mBatteryStatsHelper.setSystemPackageArray(systemPackages);
        doReturn(UID).when(mNormalBatterySipper).getUid();
        doReturn(systemPackages).when(mPackageManager).getPackagesForUid(UID);

        assertThat(mBatteryStatsHelper.isTypeSystem(mNormalBatterySipper)).isTrue();
    }

    @Test
    public void testIsTypeService_servicePackage_returnTrue() {
        final String[] servicePackages = {"com.android.service"};
        mBatteryStatsHelper.setServicePackageArray(servicePackages);
        doReturn(UID).when(mNormalBatterySipper).getUid();
        doReturn(servicePackages).when(mPackageManager).getPackagesForUid(UID);

        assertThat(mBatteryStatsHelper.isTypeService(mNormalBatterySipper)).isTrue();
    }

    private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
            int uidCode, boolean isUidNull) {
        final BatterySipper sipper = mock(BatterySipper.class);
        sipper.drainType = BatterySipper.DrainType.APP;
        sipper.totalPowerMah = totalPowerMah;
        doReturn(uidCode).when(sipper).getUid();
        if (!isUidNull) {
            final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS);
            doReturn(activityTime).when(mBatteryStatsHelper).getForegroundActivityTotalTimeMs(
                    eq(uid), anyLong());
            doReturn(uidCode).when(uid).getUid();
            sipper.uidObj = uid;
        }

        return sipper;
    }


}