1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.fuelgauge.anomaly.checker; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.nullable; 22 import static org.mockito.Matchers.any; 23 import static org.mockito.Matchers.anyInt; 24 import static org.mockito.Mockito.doReturn; 25 import static org.mockito.Mockito.spy; 26 27 import android.content.Context; 28 import android.content.pm.ApplicationInfo; 29 import android.os.BatteryStats; 30 import android.os.Build; 31 import android.text.format.DateUtils; 32 import android.util.ArrayMap; 33 import android.util.ArraySet; 34 35 import com.android.internal.os.BatterySipper; 36 import com.android.internal.os.BatteryStatsHelper; 37 import com.android.settings.fuelgauge.anomaly.AnomalyUtils; 38 import com.android.settings.testutils.SettingsRobolectricTestRunner; 39 import com.android.settings.TestConfig; 40 import com.android.settings.fuelgauge.BatteryUtils; 41 import com.android.settings.fuelgauge.anomaly.Anomaly; 42 import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy; 43 import com.android.settings.fuelgauge.anomaly.action.AnomalyAction; 44 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 import org.mockito.Mock; 49 import org.mockito.MockitoAnnotations; 50 import org.robolectric.RuntimeEnvironment; 51 import org.robolectric.annotation.Config; 52 import org.robolectric.util.ReflectionHelpers; 53 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.Set; 57 58 @RunWith(SettingsRobolectricTestRunner.class) 59 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) 60 public class WakeupAlarmAnomalyDetectorTest { 61 private static final String TARGET_PACKAGE_NAME = "com.android.target"; 62 private static final String ANOMALY_PACKAGE_NAME = "com.android.anomaly"; 63 private static final boolean TARGET_BACKGROUND_RESTRICTION_ON = false; 64 private static final boolean ANOMALY_BACKGROUND_RESTRICTION_ON = true; 65 private static final int TARGET_SDK = Build.VERSION_CODES.L; 66 private static final int ANOMALY_SDK = Build.VERSION_CODES.O; 67 private static final int ANOMALY_UID = 111; 68 private static final int NORMAL_UID = 222; 69 private static final int TARGET_UID = 333; 70 private static final long RUNNING_TIME_MS = 71 1 * DateUtils.HOUR_IN_MILLIS + 10 * DateUtils.MINUTE_IN_MILLIS; 72 private static final int ANOMALY_WAKEUP_COUNT = 500; 73 private static final int NORMAL_WAKEUP_COUNT = 61; 74 private static final int BLACKLISTED_WAKEUP_COUNT = 37; 75 private static final int ANOMALY_WAKEUP_FREQUENCY = 428; // count per hour 76 @Mock 77 private BatteryStatsHelper mBatteryStatsHelper; 78 @Mock 79 private BatterySipper mAnomalySipper; 80 @Mock 81 private BatterySipper mNormalSipper; 82 @Mock 83 private BatterySipper mTargetSipper; 84 @Mock 85 private BatteryStats.Uid mAnomalyUid; 86 @Mock 87 private BatteryStats.Uid mNormalUid; 88 @Mock 89 private BatteryStats.Uid mTargetUid; 90 @Mock 91 private BatteryUtils mBatteryUtils; 92 @Mock 93 private BatteryStats.Uid.Pkg mPkg; 94 @Mock 95 private BatteryStats.Counter mCounter; 96 @Mock 97 private BatteryStats.Counter mCounter2; 98 @Mock 99 private AnomalyDetectionPolicy mPolicy; 100 @Mock 101 private AnomalyAction mAnomalyAction; 102 @Mock 103 private AnomalyUtils mAnomalyUtils; 104 105 private WakeupAlarmAnomalyDetector mWakeupAlarmAnomalyDetector; 106 private Context mContext; 107 private List<BatterySipper> mUsageList; 108 private Anomaly mAnomaly; 109 private Anomaly mTargetAnomaly; 110 111 @Before 112 public void setUp() throws Exception { 113 MockitoAnnotations.initMocks(this); 114 115 mContext = spy(RuntimeEnvironment.application); 116 ReflectionHelpers.setField(mPolicy, "wakeupAlarmThreshold", 60); 117 final Set<String> blacklistedTags = new ArraySet<>(); 118 blacklistedTags.add("blacklistedTag"); 119 ReflectionHelpers.setField(mPolicy, "wakeupBlacklistedTags", blacklistedTags); 120 121 doReturn(false).when(mBatteryUtils).shouldHideSipper(any()); 122 doReturn(RUNNING_TIME_MS).when(mBatteryUtils).calculateRunningTimeBasedOnStatsType(any(), 123 anyInt()); 124 doReturn(true).when(mAnomalyAction).isActionActive(any()); 125 doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any()); 126 127 mAnomalySipper.uidObj = mAnomalyUid; 128 doReturn(ANOMALY_UID).when(mAnomalyUid).getUid(); 129 mNormalSipper.uidObj = mNormalUid; 130 doReturn(NORMAL_UID).when(mNormalUid).getUid(); 131 mTargetSipper.uidObj = mTargetUid; 132 doReturn(TARGET_UID).when(mTargetUid).getUid(); 133 134 mUsageList = new ArrayList<>(); 135 mUsageList.add(mAnomalySipper); 136 mUsageList.add(mNormalSipper); 137 mUsageList.add(mTargetSipper); 138 doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); 139 140 doReturn(TARGET_PACKAGE_NAME).when(mBatteryUtils).getPackageName(TARGET_UID); 141 doReturn(ANOMALY_PACKAGE_NAME).when(mBatteryUtils).getPackageName(ANOMALY_UID); 142 doReturn(TARGET_SDK).when(mBatteryUtils).getTargetSdkVersion(TARGET_PACKAGE_NAME); 143 doReturn(ANOMALY_SDK).when(mBatteryUtils).getTargetSdkVersion(ANOMALY_PACKAGE_NAME); 144 doReturn(TARGET_BACKGROUND_RESTRICTION_ON).when(mBatteryUtils) 145 .isBackgroundRestrictionEnabled(TARGET_SDK, TARGET_UID, TARGET_PACKAGE_NAME); 146 doReturn(ANOMALY_BACKGROUND_RESTRICTION_ON).when(mBatteryUtils) 147 .isBackgroundRestrictionEnabled(ANOMALY_SDK, ANOMALY_UID, ANOMALY_PACKAGE_NAME); 148 149 mAnomaly = new Anomaly.Builder() 150 .setUid(ANOMALY_UID) 151 .setPackageName(ANOMALY_PACKAGE_NAME) 152 .setType(Anomaly.AnomalyType.WAKEUP_ALARM) 153 .setTargetSdkVersion(ANOMALY_SDK) 154 .setBackgroundRestrictionEnabled(ANOMALY_BACKGROUND_RESTRICTION_ON) 155 .setWakeupAlarmCount(ANOMALY_WAKEUP_FREQUENCY) 156 .build(); 157 mTargetAnomaly = new Anomaly.Builder() 158 .setUid(TARGET_UID) 159 .setPackageName(TARGET_PACKAGE_NAME) 160 .setType(Anomaly.AnomalyType.WAKEUP_ALARM) 161 .setTargetSdkVersion(TARGET_SDK) 162 .setBackgroundRestrictionEnabled(TARGET_BACKGROUND_RESTRICTION_ON) 163 .setWakeupAlarmCount(ANOMALY_WAKEUP_FREQUENCY) 164 .build(); 165 166 mWakeupAlarmAnomalyDetector = spy( 167 new WakeupAlarmAnomalyDetector(mContext, mPolicy, mAnomalyUtils)); 168 mWakeupAlarmAnomalyDetector.mBatteryUtils = mBatteryUtils; 169 } 170 171 @Test 172 public void testDetectAnomalies_containsAnomaly_detectIt() { 173 doReturn(-1).when(mBatteryUtils).getPackageUid(nullable(String.class)); 174 doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid( 175 mAnomalyUid); 176 doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid( 177 mTargetUid); 178 doReturn(NORMAL_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid( 179 mNormalUid); 180 181 List<Anomaly> mAnomalies = mWakeupAlarmAnomalyDetector.detectAnomalies(mBatteryStatsHelper); 182 183 assertThat(mAnomalies).containsExactly(mAnomaly, mTargetAnomaly); 184 } 185 186 @Test 187 public void testDetectAnomalies_detectTargetAnomaly_detectIt() { 188 doReturn(TARGET_UID).when(mBatteryUtils).getPackageUid(TARGET_PACKAGE_NAME); 189 doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid( 190 mAnomalyUid); 191 doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid( 192 mTargetUid); 193 doReturn(NORMAL_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid( 194 mNormalUid); 195 196 List<Anomaly> mAnomalies = mWakeupAlarmAnomalyDetector.detectAnomalies(mBatteryStatsHelper, 197 TARGET_PACKAGE_NAME); 198 199 assertThat(mAnomalies).containsExactly(mTargetAnomaly); 200 } 201 202 @Test 203 public void testGetWakeupAlarmCountFromUid_countCorrect() { 204 final ArrayMap<String, BatteryStats.Uid.Pkg> packageStats = new ArrayMap<>(); 205 final ArrayMap<String, BatteryStats.Counter> alarms = new ArrayMap<>(); 206 doReturn(alarms).when(mPkg).getWakeupAlarmStats(); 207 doReturn(NORMAL_WAKEUP_COUNT).when(mCounter).getCountLocked(anyInt()); 208 doReturn(packageStats).when(mAnomalyUid).getPackageStats(); 209 packageStats.put("", mPkg); 210 alarms.put("1", mCounter); 211 alarms.put("2", mCounter); 212 213 assertThat(mWakeupAlarmAnomalyDetector.getWakeupAlarmCountFromUid(mAnomalyUid)).isEqualTo( 214 2 * NORMAL_WAKEUP_COUNT); 215 } 216 217 @Test 218 public void testGetWakeupAlarmCountFromUid_filterOutBlacklistedTags() { 219 final ArrayMap<String, BatteryStats.Uid.Pkg> packageStats = new ArrayMap<>(); 220 final ArrayMap<String, BatteryStats.Counter> alarms = new ArrayMap<>(); 221 doReturn(alarms).when(mPkg).getWakeupAlarmStats(); 222 doReturn(NORMAL_WAKEUP_COUNT).when(mCounter).getCountLocked(anyInt()); 223 doReturn(BLACKLISTED_WAKEUP_COUNT).when(mCounter2).getCountLocked(anyInt()); 224 doReturn(packageStats).when(mAnomalyUid).getPackageStats(); 225 packageStats.put("", mPkg); 226 alarms.put("allowedTag", mCounter); 227 alarms.put("blacklistedTag", mCounter2); 228 229 assertThat(mWakeupAlarmAnomalyDetector.getWakeupAlarmCountFromUid(mAnomalyUid)).isEqualTo( 230 NORMAL_WAKEUP_COUNT); 231 } 232 } 233