1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  *
16  */
17 
18 package com.android.internal.os;
19 
20 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static org.mockito.Matchers.anyInt;
25 import static org.mockito.Matchers.anyLong;
26 import static org.mockito.Matchers.eq;
27 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.spy;
31 import static org.mockito.Mockito.when;
32 
33 import android.content.Context;
34 import android.content.pm.PackageManager;
35 import android.os.BatteryStats;
36 import android.os.Process;
37 import android.text.format.DateUtils;
38 
39 import androidx.test.InstrumentationRegistry;
40 import androidx.test.filters.SmallTest;
41 import androidx.test.runner.AndroidJUnit4;
42 
43 import com.android.internal.util.FrameworkStatsLog;
44 
45 import junit.framework.TestCase;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.mockito.Mock;
51 import org.mockito.MockitoAnnotations;
52 
53 import java.util.ArrayList;
54 import java.util.List;
55 
56 @RunWith(AndroidJUnit4.class)
57 @SmallTest
58 public class BatteryStatsHelperTest extends TestCase {
59     private static final long TIME_FOREGROUND_ACTIVITY_ZERO = 0;
60     private static final long TIME_FOREGROUND_ACTIVITY = 100 * DateUtils.MINUTE_IN_MILLIS * 1000;
61     private static final long TIME_STATE_FOREGROUND_MS = 10 * DateUtils.MINUTE_IN_MILLIS;
62     private static final long TIME_STATE_FOREGROUND_US = TIME_STATE_FOREGROUND_MS * 1000;
63 
64     private static final int UID = 123456;
65     private static final double BATTERY_SCREEN_USAGE = 300;
66     private static final double BATTERY_SYSTEM_USAGE = 600;
67     private static final double BATTERY_WIFI_USAGE = 200;
68     private static final double BATTERY_IDLE_USAGE = 600;
69     private static final double BATTERY_BLUETOOTH_USAGE = 300;
70     private static final double BATTERY_OVERACCOUNTED_USAGE = 500;
71     private static final double BATTERY_UNACCOUNTED_USAGE = 700;
72     private static final double BATTERY_APP_USAGE = 100;
73     private static final double TOTAL_BATTERY_USAGE = 1000;
74     private static final double PRECISION = 0.001;
75 
76     @Mock
77     private BatteryStats.Uid mUid;
78     @Mock
79     private BatterySipper mWifiBatterySipper;
80     @Mock
81     private BatterySipper mBluetoothBatterySipper;
82     @Mock
83     private BatterySipper mIdleBatterySipper;
84     @Mock
85     private BatterySipper mNormalBatterySipper;
86     @Mock
87     private BatterySipper mScreenBatterySipper;
88     @Mock
89     private BatterySipper mOvercountedBatterySipper;
90     @Mock
91     private BatterySipper mUnaccountedBatterySipper;
92     @Mock
93     private BatterySipper mSystemBatterySipper;
94     @Mock
95     private BatterySipper mCellBatterySipper;
96     @Mock
97     private PackageManager mPackageManager;
98 
99     private BatteryStatsHelper mBatteryStatsHelper;
100     private Context mContext;
101 
102     @Before
setUp()103     public void setUp() {
104         MockitoAnnotations.initMocks(this);
105 
106         mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
107         mNormalBatterySipper.totalPowerMah = TOTAL_BATTERY_USAGE;
108         when(mNormalBatterySipper.getUid()).thenReturn(UID);
109         mNormalBatterySipper.uidObj = mUid;
110 
111 
112         mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
113         mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE;
114 
115         mSystemBatterySipper.drainType = BatterySipper.DrainType.APP;
116         mSystemBatterySipper.totalPowerMah = BATTERY_SYSTEM_USAGE;
117         mSystemBatterySipper.uidObj = mUid;
118         when(mSystemBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID);
119 
120         mOvercountedBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
121         mOvercountedBatterySipper.totalPowerMah = BATTERY_OVERACCOUNTED_USAGE;
122 
123         mUnaccountedBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
124         mUnaccountedBatterySipper.totalPowerMah = BATTERY_UNACCOUNTED_USAGE;
125 
126         mWifiBatterySipper.drainType = BatterySipper.DrainType.WIFI;
127         mWifiBatterySipper.totalPowerMah = BATTERY_WIFI_USAGE;
128 
129         mBluetoothBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
130         mBluetoothBatterySipper.totalPowerMah = BATTERY_BLUETOOTH_USAGE;
131 
132         mIdleBatterySipper.drainType = BatterySipper.DrainType.IDLE;
133         mIdleBatterySipper.totalPowerMah = BATTERY_IDLE_USAGE;
134 
135         mContext = InstrumentationRegistry.getContext();
136         mBatteryStatsHelper = spy(new BatteryStatsHelper(mContext));
137         mBatteryStatsHelper.setPackageManager(mPackageManager);
138     }
139 
140     @Test
testShouldHideSipper_TypeUnAccounted_ReturnTrue()141     public void testShouldHideSipper_TypeUnAccounted_ReturnTrue() {
142         mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
143         assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
144     }
145 
146     @Test
testShouldHideSipper_TypeOverAccounted_ReturnTrue()147     public void testShouldHideSipper_TypeOverAccounted_ReturnTrue() {
148         mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
149         assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
150     }
151 
152     @Test
testShouldHideSipper_TypeIdle_ReturnTrue()153     public void testShouldHideSipper_TypeIdle_ReturnTrue() {
154         mNormalBatterySipper.drainType = BatterySipper.DrainType.IDLE;
155         assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
156     }
157 
158     @Test
testShouldHideSipper_TypeCell_ReturnTrue()159     public void testShouldHideSipper_TypeCell_ReturnTrue() {
160         mNormalBatterySipper.drainType = BatterySipper.DrainType.CELL;
161         assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
162     }
163 
164     @Test
testShouldHideSipper_TypeScreen_ReturnTrue()165     public void testShouldHideSipper_TypeScreen_ReturnTrue() {
166         mNormalBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
167         assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
168     }
169 
170     @Test
testShouldHideSipper_TypeSystem_ReturnTrue()171     public void testShouldHideSipper_TypeSystem_ReturnTrue() {
172         mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
173         when(mNormalBatterySipper.getUid()).thenReturn(Process.ROOT_UID);
174         assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
175     }
176 
177     @Test
testShouldHideSipper_UidNormal_ReturnFalse()178     public void testShouldHideSipper_UidNormal_ReturnFalse() {
179         mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
180         assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isFalse();
181     }
182 
183     @Test
testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue()184     public void testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue() {
185         final List<BatterySipper> sippers = new ArrayList<>();
186         sippers.add(mNormalBatterySipper);
187         sippers.add(mScreenBatterySipper);
188         sippers.add(mSystemBatterySipper);
189         sippers.add(mOvercountedBatterySipper);
190         sippers.add(mUnaccountedBatterySipper);
191         sippers.add(mWifiBatterySipper);
192         sippers.add(mBluetoothBatterySipper);
193         sippers.add(mIdleBatterySipper);
194         doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper);
195 
196         final double totalUsage = mBatteryStatsHelper.removeHiddenBatterySippers(sippers);
197 
198         assertThat(mNormalBatterySipper.shouldHide).isFalse();
199         assertThat(mScreenBatterySipper.shouldHide).isTrue();
200         assertThat(mSystemBatterySipper.shouldHide).isTrue();
201         assertThat(mOvercountedBatterySipper.shouldHide).isTrue();
202         assertThat(mUnaccountedBatterySipper.shouldHide).isTrue();
203         assertThat(totalUsage).isWithin(PRECISION).of(BATTERY_SYSTEM_USAGE);
204     }
205 
206     @Test
testSmearScreenBatterySipper()207     public void testSmearScreenBatterySipper() {
208         final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class);
209         final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
210                 BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */, spc);
211         final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
212                 BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */, spc);
213         final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY,
214                 BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */, spc);
215 
216         final List<BatterySipper> sippers = new ArrayList<>();
217         sippers.add(sipperNull);
218         sippers.add(sipperBg);
219         sippers.add(sipperFg);
220 
221         spc.smearScreenBatterySipper(sippers, mScreenBatterySipper, 0);
222 
223         assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0);
224         assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0);
225         assertThat(sipperFg.screenPowerMah).isWithin(PRECISION).of(BATTERY_SCREEN_USAGE);
226     }
227 
228     @Test
testIsTypeSystem_systemPackage_returnTrue()229     public void testIsTypeSystem_systemPackage_returnTrue() {
230         final String[] systemPackages = {"com.android.system"};
231         mBatteryStatsHelper.setSystemPackageArray(systemPackages);
232         doReturn(UID).when(mNormalBatterySipper).getUid();
233         doReturn(systemPackages).when(mPackageManager).getPackagesForUid(UID);
234 
235         assertThat(mBatteryStatsHelper.isTypeSystem(mNormalBatterySipper)).isTrue();
236     }
237 
238     @Test
testIsTypeService_servicePackage_returnTrue()239     public void testIsTypeService_servicePackage_returnTrue() {
240         final String[] servicePackages = {"com.android.service"};
241         mBatteryStatsHelper.setServicePackageArray(servicePackages);
242         doReturn(UID).when(mNormalBatterySipper).getUid();
243         doReturn(servicePackages).when(mPackageManager).getPackagesForUid(UID);
244 
245         assertThat(mBatteryStatsHelper.isTypeService(mNormalBatterySipper)).isTrue();
246     }
247 
248     @Test
testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime()249     public void testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime() {
250         final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class);
251         doReturn(TIME_STATE_FOREGROUND_US + 500).when(spc)
252                 .getForegroundActivityTotalTimeUs(eq(mUid), anyLong());
253         doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP),
254                 anyLong(), anyInt());
255 
256         final long time = spc.getProcessForegroundTimeMs(mUid, 1000);
257 
258         assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
259     }
260 
261     @Test
testDrainTypesSyncedWithProto()262     public void testDrainTypesSyncedWithProto() {
263         assertEquals(BatterySipper.DrainType.AMBIENT_DISPLAY.ordinal(),
264                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__AMBIENT_DISPLAY);
265         // AtomsProto has no "APP"
266         assertEquals(BatterySipper.DrainType.BLUETOOTH.ordinal(),
267                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__BLUETOOTH);
268         assertEquals(BatterySipper.DrainType.CAMERA.ordinal(),
269                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CAMERA);
270         assertEquals(BatterySipper.DrainType.CELL.ordinal(),
271                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CELL);
272         assertEquals(BatterySipper.DrainType.FLASHLIGHT.ordinal(),
273                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__FLASHLIGHT);
274         assertEquals(BatterySipper.DrainType.IDLE.ordinal(),
275                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__IDLE);
276         assertEquals(BatterySipper.DrainType.MEMORY.ordinal(),
277                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__MEMORY);
278         assertEquals(BatterySipper.DrainType.OVERCOUNTED.ordinal(),
279                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__OVERCOUNTED);
280         assertEquals(BatterySipper.DrainType.PHONE.ordinal(),
281                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__PHONE);
282         assertEquals(BatterySipper.DrainType.SCREEN.ordinal(),
283                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__SCREEN);
284         assertEquals(BatterySipper.DrainType.UNACCOUNTED.ordinal(),
285                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__UNACCOUNTED);
286         // AtomsProto has no "USER"
287         assertEquals(BatterySipper.DrainType.WIFI.ordinal(),
288                 FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__WIFI);
289     }
290 
createTestSmearBatterySipper(long activityTime, double totalPowerMah, int uidCode, boolean isUidNull, ScreenPowerCalculator spc)291     private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
292             int uidCode, boolean isUidNull, ScreenPowerCalculator spc) {
293         final BatterySipper sipper = mock(BatterySipper.class);
294         sipper.drainType = BatterySipper.DrainType.APP;
295         sipper.totalPowerMah = totalPowerMah;
296         doReturn(uidCode).when(sipper).getUid();
297         if (!isUidNull) {
298             final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS);
299             doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid), anyLong());
300             doReturn(uidCode).when(uid).getUid();
301             sipper.uidObj = uid;
302         }
303 
304         return sipper;
305     }
306 
307 }
308