Merge "Refactor anomaly detection"

This commit is contained in:
TreeHugger Robot
2017-06-06 19:44:18 +00:00
committed by Android (Google) Code Review
11 changed files with 254 additions and 37 deletions

View File

@@ -41,6 +41,7 @@ import java.util.List;
* Utils for battery operation * Utils for battery operation
*/ */
public class BatteryUtils { public class BatteryUtils {
public static final int UID_NULL = -1;
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({StatusType.FOREGROUND, @IntDef({StatusType.FOREGROUND,
StatusType.BACKGROUND, StatusType.BACKGROUND,
@@ -274,6 +275,22 @@ public class BatteryUtils {
} }
/**
* Find package uid from package name
*
* @param packageName used to find the uid
* @return uid for packageName, or {@link #UID_NULL} if exception happens or
* {@code packageName} is null
*/
public int getPackageUid(String packageName) {
try {
return packageName == null ? UID_NULL : mPackageManager.getPackageUid(packageName,
PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
return UID_NULL;
}
}
public long convertUsToMs(long timeUs) { public long convertUsToMs(long timeUs) {
return timeUs / 1000; return timeUs / 1000;
} }

View File

@@ -90,6 +90,7 @@ public interface PowerUsageFeatureProvider {
/** /**
* Check whether a specific anomaly detector is enabled * Check whether a specific anomaly detector is enabled
*/ */
//TODO(b/62096650): remove this method and use AnomalyDetectionPolicy instead
boolean isAnomalyDetectorEnabled(@Anomaly.AnomalyType int type); boolean isAnomalyDetectorEnabled(@Anomaly.AnomalyType int type);
/** /**

View File

@@ -108,4 +108,14 @@ public class AnomalyDetectionPolicy {
wakeupAlarmThreshold = mParserWrapper.getLong(KEY_WAKEUP_ALARM_THRESHOLD, 60); wakeupAlarmThreshold = mParserWrapper.getLong(KEY_WAKEUP_ALARM_THRESHOLD, 60);
} }
public boolean isAnomalyDetectorEnabled(@Anomaly.AnomalyType int type) {
switch (type) {
case Anomaly.AnomalyType.WAKE_LOCK:
return wakeLockDetectionEnabled;
case Anomaly.AnomalyType.WAKEUP_ALARM:
return wakeupAlarmDetectionEnabled;
default:
return false; // Disabled when no this type
}
}
} }

View File

@@ -17,11 +17,12 @@
package com.android.settings.fuelgauge.anomaly; package com.android.settings.fuelgauge.anomaly;
import android.content.Context; import android.content.Context;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.AsyncLoader; import com.android.settings.utils.AsyncLoader;
import java.util.ArrayList; import java.util.ArrayList;
@@ -33,16 +34,45 @@ import java.util.List;
*/ */
public class AnomalyLoader extends AsyncLoader<List<Anomaly>> { public class AnomalyLoader extends AsyncLoader<List<Anomaly>> {
private BatteryStatsHelper mBatteryStatsHelper; private BatteryStatsHelper mBatteryStatsHelper;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider; private String mPackageName;
private UserManager mUserManager;
@VisibleForTesting @VisibleForTesting
AnomalyUtils mAnomalyUtils; AnomalyUtils mAnomalyUtils;
@VisibleForTesting
AnomalyDetectionPolicy mPolicy;
/**
* Create {@link AnomalyLoader} that runs anomaly check for all apps.
*/
public AnomalyLoader(Context context, BatteryStatsHelper batteryStatsHelper) { public AnomalyLoader(Context context, BatteryStatsHelper batteryStatsHelper) {
this(context, batteryStatsHelper, null, new AnomalyDetectionPolicy(context));
}
/**
* Create {@link AnomalyLoader} with {@code packageName}, so this loader will only
* detect anomalies related to {@code packageName}, or check all apps if {@code packageName}
* is {@code null}.
*
* This constructor will create {@link BatteryStatsHelper} in background thread.
*
* @param context
* @param packageName if set, only finds anomalies for this package. If {@code null},
* detects all anomalies of this type.
*/
public AnomalyLoader(Context context, String packageName) {
this(context, null, packageName, new AnomalyDetectionPolicy(context));
}
@VisibleForTesting
AnomalyLoader(Context context, BatteryStatsHelper batteryStatsHelper,
String packageName, AnomalyDetectionPolicy policy) {
super(context); super(context);
mBatteryStatsHelper = batteryStatsHelper; mBatteryStatsHelper = batteryStatsHelper;
mPowerUsageFeatureProvider = FeatureFactory.getFactory( mPackageName = packageName;
context).getPowerUsageFeatureProvider(context);
mAnomalyUtils = AnomalyUtils.getInstance(context); mAnomalyUtils = AnomalyUtils.getInstance(context);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mPolicy = policy;
} }
@Override @Override
@@ -51,11 +81,18 @@ public class AnomalyLoader extends AsyncLoader<List<Anomaly>> {
@Override @Override
public List<Anomaly> loadInBackground() { public List<Anomaly> loadInBackground() {
if (mBatteryStatsHelper == null) {
mBatteryStatsHelper = new BatteryStatsHelper(getContext());
mBatteryStatsHelper.create((Bundle) null);
mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
mUserManager.getUserProfiles());
}
final List<Anomaly> anomalies = new ArrayList<>(); final List<Anomaly> anomalies = new ArrayList<>();
for (@Anomaly.AnomalyType int type : Anomaly.ANOMALY_TYPE_LIST) { for (@Anomaly.AnomalyType int type : Anomaly.ANOMALY_TYPE_LIST) {
if (mPowerUsageFeatureProvider.isAnomalyDetectorEnabled(type)) { if (mPolicy.isAnomalyDetectorEnabled(type)) {
anomalies.addAll(mAnomalyUtils.getAnomalyDetector(type).detectAnomalies( anomalies.addAll(mAnomalyUtils.getAnomalyDetector(type).detectAnomalies(
mBatteryStatsHelper)); mBatteryStatsHelper, mPackageName));
} }
} }

View File

@@ -17,11 +17,30 @@
package com.android.settings.fuelgauge.anomaly.checker; package com.android.settings.fuelgauge.anomaly.checker;
import android.annotation.Nullable;
import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.anomaly.Anomaly;
import java.util.List; import java.util.List;
public interface AnomalyDetector { public interface AnomalyDetector {
/**
* Detect whether there is anomaly among all the applications in the device
*
* @param batteryStatsHelper used to detect the anomaly
* @return anomaly list
*/
List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper); List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper);
/**
* Detect whether application with {@code targetPackageName} has anomaly. When
* {@code targetPackageName} is null, start detection among all the applications.
*
* @param batteryStatsHelper used to detect the anomaly
* @param targetPackageName represents the app need to be detected
* @return anomaly list
*/
List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper,
@Nullable String targetPackageName);
} }

View File

@@ -63,17 +63,27 @@ public class WakeLockAnomalyDetector implements AnomalyDetector {
@Override @Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) { public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) {
return detectAnomalies(batteryStatsHelper, null);
}
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper,
String targetPackageName) {
final List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList(); final List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList();
final List<Anomaly> anomalies = new ArrayList<>(); final List<Anomaly> anomalies = new ArrayList<>();
final long rawRealtime = SystemClock.elapsedRealtime(); final long rawRealtime = SystemClock.elapsedRealtime();
final int targetUid = mBatteryUtils.getPackageUid(targetPackageName);
// Check the app one by one // Check the app one by one
for (int i = 0, size = batterySippers.size(); i < size; i++) { for (int i = 0, size = batterySippers.size(); i < size; i++) {
final BatterySipper sipper = batterySippers.get(i); final BatterySipper sipper = batterySippers.get(i);
final BatteryStats.Uid uid = sipper.uidObj; final BatteryStats.Uid uid = sipper.uidObj;
if (uid == null) { if (uid == null
|| mBatteryUtils.shouldHideSipper(sipper)
|| (targetUid != BatteryUtils.UID_NULL && targetUid != uid.getUid())) {
continue; continue;
} }
final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks =
uid.getWakelockStats(); uid.getWakelockStats();
long maxPartialWakeLockMs = 0; long maxPartialWakeLockMs = 0;
@@ -88,10 +98,9 @@ public class WakeLockAnomalyDetector implements AnomalyDetector {
getTotalDurationMs(timer, rawRealtime)); getTotalDurationMs(timer, rawRealtime));
} }
// Report it if wakelock time is too long and it is not a hidden batterysipper // Report application as anomaly if wakelock time is too long
// TODO: add more attributes to detect wakelock anomaly // TODO(b/38233034): add more attributes to detect wakelock anomaly
if (maxPartialWakeLockMs > mWakeLockThresholdMs if (maxPartialWakeLockMs > mWakeLockThresholdMs) {
&& !mBatteryUtils.shouldHideSipper(sipper)) {
final String packageName = mBatteryUtils.getPackageName(uid.getUid()); final String packageName = mBatteryUtils.getPackageName(uid.getUid());
final CharSequence displayName = Utils.getApplicationLabel(mContext, final CharSequence displayName = Utils.getApplicationLabel(mContext,
packageName); packageName);

View File

@@ -57,16 +57,25 @@ public class WakeupAlarmAnomalyDetector implements AnomalyDetector {
@Override @Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) { public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) {
return detectAnomalies(batteryStatsHelper, null);
}
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper,
String targetPackageName) {
final List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList(); final List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList();
final List<Anomaly> anomalies = new ArrayList<>(); final List<Anomaly> anomalies = new ArrayList<>();
final long totalRunningHours = mBatteryUtils.calculateRunningTimeBasedOnStatsType( final long totalRunningHours = mBatteryUtils.calculateRunningTimeBasedOnStatsType(
batteryStatsHelper, BatteryStats.STATS_SINCE_CHARGED) / DateUtils.HOUR_IN_MILLIS; batteryStatsHelper, BatteryStats.STATS_SINCE_CHARGED) / DateUtils.HOUR_IN_MILLIS;
final int targetUid = mBatteryUtils.getPackageUid(targetPackageName);
if (totalRunningHours != 0) { if (totalRunningHours != 0) {
for (int i = 0, size = batterySippers.size(); i < size; i++) { for (int i = 0, size = batterySippers.size(); i < size; i++) {
final BatterySipper sipper = batterySippers.get(i); final BatterySipper sipper = batterySippers.get(i);
final BatteryStats.Uid uid = sipper.uidObj; final BatteryStats.Uid uid = sipper.uidObj;
if (uid == null || mBatteryUtils.shouldHideSipper(sipper)) { if (uid == null
|| mBatteryUtils.shouldHideSipper(sipper)
|| (targetUid != BatteryUtils.UID_NULL && targetUid != uid.getUid())) {
continue; continue;
} }

View File

@@ -36,6 +36,7 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -57,18 +58,7 @@ public class AnomalyDetectionPolicyTest {
@Test @Test
public void testInit_containsDataFromSettings() { public void testInit_containsDataFromSettings() {
Settings.Global.putString(mContext.getContentResolver(), AnomalyDetectionPolicy anomalyDetectionPolicy = createAnomalyPolicyWithConfig();
Settings.Global.ANOMALY_DETECTION_CONSTANTS, ANOMALY_DETECTION_CONSTANTS_VALUE);
// Mock it to avoid noSuchMethodError
doReturn(true).when(mKeyValueListParserWrapper).getBoolean(
AnomalyDetectionPolicy.KEY_ANOMALY_DETECTION_ENABLED, true);
doReturn(false).when(mKeyValueListParserWrapper).getBoolean(
AnomalyDetectionPolicy.KEY_WAKELOCK_DETECTION_ENABLED, true);
doReturn(true).when(mKeyValueListParserWrapper).getBoolean(
AnomalyDetectionPolicy.KEY_WAKEUP_ALARM_DETECTION_ENABLED, true);
AnomalyDetectionPolicy anomalyDetectionPolicy = new AnomalyDetectionPolicy(mContext,
mKeyValueListParserWrapper);
assertThat(anomalyDetectionPolicy.anomalyDetectionEnabled).isTrue(); assertThat(anomalyDetectionPolicy.anomalyDetectionEnabled).isTrue();
assertThat(anomalyDetectionPolicy.wakeLockDetectionEnabled).isFalse(); assertThat(anomalyDetectionPolicy.wakeLockDetectionEnabled).isFalse();
@@ -94,4 +84,30 @@ public class AnomalyDetectionPolicyTest {
assertThat(anomalyDetectionPolicy.wakeupAlarmDetectionEnabled).isTrue(); assertThat(anomalyDetectionPolicy.wakeupAlarmDetectionEnabled).isTrue();
assertThat(anomalyDetectionPolicy.wakeupAlarmThreshold).isEqualTo(60); assertThat(anomalyDetectionPolicy.wakeupAlarmThreshold).isEqualTo(60);
} }
@Test
public void testIsAnomalyDetectorEnabled() {
AnomalyDetectionPolicy anomalyDetectionPolicy = createAnomalyPolicyWithConfig();
assertThat(anomalyDetectionPolicy.isAnomalyDetectorEnabled(
Anomaly.AnomalyType.WAKE_LOCK)).isFalse();
assertThat(anomalyDetectionPolicy.isAnomalyDetectorEnabled(
Anomaly.AnomalyType.WAKEUP_ALARM)).isTrue();
}
private AnomalyDetectionPolicy createAnomalyPolicyWithConfig() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.ANOMALY_DETECTION_CONSTANTS, ANOMALY_DETECTION_CONSTANTS_VALUE);
// Mock it to avoid noSuchMethodError
doReturn(true).when(mKeyValueListParserWrapper).getBoolean(
AnomalyDetectionPolicy.KEY_ANOMALY_DETECTION_ENABLED, true);
doReturn(false).when(mKeyValueListParserWrapper).getBoolean(
AnomalyDetectionPolicy.KEY_WAKELOCK_DETECTION_ENABLED, true);
doReturn(true).when(mKeyValueListParserWrapper).getBoolean(
AnomalyDetectionPolicy.KEY_WAKEUP_ALARM_DETECTION_ENABLED, true);
return new AnomalyDetectionPolicy(mContext, mKeyValueListParserWrapper);
}
} }

View File

@@ -19,16 +19,19 @@ package com.android.settings.fuelgauge.anomaly;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.os.UserManager;
import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeupAlarmAnomalyDetector;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before; import org.junit.Before;
@@ -51,25 +54,39 @@ public class AnomalyLoaderTest {
private BatteryStatsHelper mBatteryStatsHelper; private BatteryStatsHelper mBatteryStatsHelper;
@Mock @Mock
private WakeLockAnomalyDetector mWakeLockAnomalyDetector; private WakeLockAnomalyDetector mWakeLockAnomalyDetector;
@Mock
private WakeupAlarmAnomalyDetector mWakeupAlarmAnomalyDetector;
@Mock
private AnomalyDetectionPolicy mAnomalyDetectionPolicy;
@Mock
private UserManager mUserManager;
private Anomaly mWakeLockAnomaly; private Anomaly mWakeLockAnomaly;
private Anomaly mWakeupAlarmAnomaly;
private List<Anomaly> mWakeLockAnomalies; private List<Anomaly> mWakeLockAnomalies;
private List<Anomaly> mWakeupAlarmAnomalies;
private AnomalyLoader mAnomalyLoader; private AnomalyLoader mAnomalyLoader;
private FakeFeatureFactory mFeatureFactory;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest(mContext); FakeFeatureFactory.setupForTest(mContext);
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); doReturn(true).when(mAnomalyDetectionPolicy).isAnomalyDetectorEnabled(anyInt());
doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
mWakeLockAnomalies = new ArrayList<>(); mWakeLockAnomalies = new ArrayList<>();
mWakeLockAnomaly = new Anomaly.Builder() mWakeLockAnomaly = createAnomaly(Anomaly.AnomalyType.WAKE_LOCK);
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.build();
mWakeLockAnomalies.add(mWakeLockAnomaly); mWakeLockAnomalies.add(mWakeLockAnomaly);
doReturn(mWakeLockAnomalies).when(mWakeLockAnomalyDetector).detectAnomalies(any(), any());
mAnomalyLoader = new AnomalyLoader(mContext, mBatteryStatsHelper); mWakeupAlarmAnomalies = new ArrayList<>();
mWakeupAlarmAnomaly = createAnomaly(Anomaly.AnomalyType.WAKEUP_ALARM);
mWakeupAlarmAnomalies.add(mWakeupAlarmAnomaly);
doReturn(mWakeupAlarmAnomalies).when(mWakeupAlarmAnomalyDetector).detectAnomalies(any(),
any());
mAnomalyLoader = new AnomalyLoader(mContext, mBatteryStatsHelper, null,
mAnomalyDetectionPolicy);
mAnomalyLoader.mAnomalyUtils = spy(new AnomalyUtils(mContext)); mAnomalyLoader.mAnomalyUtils = spy(new AnomalyUtils(mContext));
} }
@@ -77,12 +94,17 @@ public class AnomalyLoaderTest {
public void testLoadInBackground_containsValidAnomalies() { public void testLoadInBackground_containsValidAnomalies() {
doReturn(mWakeLockAnomalyDetector).when(mAnomalyLoader.mAnomalyUtils).getAnomalyDetector( doReturn(mWakeLockAnomalyDetector).when(mAnomalyLoader.mAnomalyUtils).getAnomalyDetector(
Anomaly.AnomalyType.WAKE_LOCK); Anomaly.AnomalyType.WAKE_LOCK);
doReturn(mWakeLockAnomalies).when(mWakeLockAnomalyDetector).detectAnomalies(any()); doReturn(mWakeupAlarmAnomalyDetector).when(mAnomalyLoader.mAnomalyUtils).getAnomalyDetector(
when(mFeatureFactory.powerUsageFeatureProvider.isAnomalyDetectorEnabled( Anomaly.AnomalyType.WAKEUP_ALARM);
Anomaly.AnomalyType.WAKE_LOCK)).thenReturn(true);
List<Anomaly> anomalies = mAnomalyLoader.loadInBackground(); List<Anomaly> anomalies = mAnomalyLoader.loadInBackground();
assertThat(anomalies).containsExactly(mWakeLockAnomaly); assertThat(anomalies).containsExactly(mWakeLockAnomaly, mWakeupAlarmAnomaly);
}
private Anomaly createAnomaly(@Anomaly.AnomalyType int type) {
return new Anomaly.Builder()
.setType(type)
.build();
} }
} }

View File

@@ -55,16 +55,20 @@ import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class WakeLockAnomalyDetectorTest { public class WakeLockAnomalyDetectorTest {
private static final String TARGET_PACKAGE_NAME = "com.android.app";
private static final long ANOMALY_WAKELOCK_TIME_MS = 2 * DateUtils.HOUR_IN_MILLIS; private static final long ANOMALY_WAKELOCK_TIME_MS = 2 * DateUtils.HOUR_IN_MILLIS;
private static final long NORMAL_WAKELOCK_TIME_MS = DateUtils.SECOND_IN_MILLIS; private static final long NORMAL_WAKELOCK_TIME_MS = DateUtils.SECOND_IN_MILLIS;
private static final long WAKELOCK_THRESHOLD_MS = DateUtils.HOUR_IN_MILLIS; private static final long WAKELOCK_THRESHOLD_MS = DateUtils.HOUR_IN_MILLIS;
private static final int ANOMALY_UID = 111; private static final int ANOMALY_UID = 111;
private static final int NORMAL_UID = 222; private static final int NORMAL_UID = 222;
private static final int TARGET_UID = 333;
@Mock @Mock
private BatteryStatsHelper mBatteryStatsHelper; private BatteryStatsHelper mBatteryStatsHelper;
@Mock @Mock
private BatterySipper mAnomalySipper; private BatterySipper mAnomalySipper;
@Mock @Mock
private BatterySipper mTargetSipper;
@Mock
private BatteryStats.Timer mAnomalyTimer; private BatteryStats.Timer mAnomalyTimer;
@Mock @Mock
private BatteryStats.Uid.Wakelock mAnomalyWakelock; private BatteryStats.Uid.Wakelock mAnomalyWakelock;
@@ -73,12 +77,18 @@ public class WakeLockAnomalyDetectorTest {
@Mock @Mock
private BatteryStats.Timer mNormalTimer; private BatteryStats.Timer mNormalTimer;
@Mock @Mock
private BatteryStats.Timer mTargetTimer;
@Mock
private BatteryStats.Uid.Wakelock mNormalWakelock; private BatteryStats.Uid.Wakelock mNormalWakelock;
@Mock @Mock
private BatteryStats.Uid.Wakelock mTargetWakelock;
@Mock
private BatteryStats.Uid mAnomalyUid; private BatteryStats.Uid mAnomalyUid;
@Mock @Mock
private BatteryStats.Uid mNormalUid; private BatteryStats.Uid mNormalUid;
@Mock @Mock
private BatteryStats.Uid mTargetUid;
@Mock
private BatteryUtils mBatteryUtils; private BatteryUtils mBatteryUtils;
@Mock @Mock
private PackageManager mPackageManager; private PackageManager mPackageManager;
@@ -89,6 +99,7 @@ public class WakeLockAnomalyDetectorTest {
private ArrayMap<String, BatteryStats.Uid.Wakelock> mAnomalyWakelocks; private ArrayMap<String, BatteryStats.Uid.Wakelock> mAnomalyWakelocks;
private ArrayMap<String, BatteryStats.Uid.Wakelock> mNormalWakelocks; private ArrayMap<String, BatteryStats.Uid.Wakelock> mNormalWakelocks;
private ArrayMap<String, BatteryStats.Uid.Wakelock> mTargetWakelocks;
private WakeLockAnomalyDetector mWakelockAnomalyDetector; private WakeLockAnomalyDetector mWakelockAnomalyDetector;
private Context mContext; private Context mContext;
private List<BatterySipper> mUsageList; private List<BatterySipper> mUsageList;
@@ -119,29 +130,58 @@ public class WakeLockAnomalyDetectorTest {
doReturn(mNormalWakelocks).when(mNormalUid).getWakelockStats(); doReturn(mNormalWakelocks).when(mNormalUid).getWakelockStats();
doReturn(NORMAL_UID).when(mNormalUid).getUid(); doReturn(NORMAL_UID).when(mNormalUid).getUid();
mTargetSipper.uidObj = mTargetUid;
mTargetWakelocks = new ArrayMap<>();
mTargetWakelocks.put("", mTargetWakelock);
doReturn(mTargetTimer).when(mTargetWakelock).getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
doReturn(mTargetWakelocks).when(mTargetUid).getWakelockStats();
doReturn(TARGET_UID).when(mTargetUid).getUid();
mUsageList = new ArrayList<>(); mUsageList = new ArrayList<>();
mUsageList.add(mAnomalySipper); mUsageList.add(mAnomalySipper);
mUsageList.add(mNormalSipper); mUsageList.add(mNormalSipper);
mUsageList.add(mTargetSipper);
doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
mWakelockAnomalyDetector = spy(new WakeLockAnomalyDetector(mContext, mPolicy)); mWakelockAnomalyDetector = spy(new WakeLockAnomalyDetector(mContext, mPolicy));
mWakelockAnomalyDetector.mBatteryUtils = mBatteryUtils; mWakelockAnomalyDetector.mBatteryUtils = mBatteryUtils;
doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector).getTotalDurationMs( doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector).getTotalDurationMs(
eq(mAnomalyTimer), anyLong()); eq(mAnomalyTimer), anyLong());
doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector).getTotalDurationMs(
eq(mTargetTimer), anyLong());
doReturn(NORMAL_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector).getTotalDurationMs( doReturn(NORMAL_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector).getTotalDurationMs(
eq(mNormalTimer), anyLong()); eq(mNormalTimer), anyLong());
} }
@Test @Test
public void testDetectAnomalies_containsAnomaly_detectIt() { public void testDetectAnomalies_containsAnomaly_detectIt() {
doReturn(BatteryUtils.UID_NULL).when(mBatteryUtils).getPackageUid(nullable(String.class));
final Anomaly anomaly = new Anomaly.Builder() final Anomaly anomaly = new Anomaly.Builder()
.setUid(ANOMALY_UID) .setUid(ANOMALY_UID)
.setType(Anomaly.AnomalyType.WAKE_LOCK) .setType(Anomaly.AnomalyType.WAKE_LOCK)
.build(); .build();
final Anomaly targetAnomaly = new Anomaly.Builder()
.setUid(TARGET_UID)
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.build();
List<Anomaly> mAnomalies = mWakelockAnomalyDetector.detectAnomalies(mBatteryStatsHelper); List<Anomaly> mAnomalies = mWakelockAnomalyDetector.detectAnomalies(mBatteryStatsHelper);
assertThat(mAnomalies).containsExactly(anomaly); assertThat(mAnomalies).containsExactly(anomaly, targetAnomaly);
}
@Test
public void testDetectAnomalies_containsTargetpackage_detectIt() {
doReturn(TARGET_UID).when(mBatteryUtils).getPackageUid(TARGET_PACKAGE_NAME);
final Anomaly targetAnomaly = new Anomaly.Builder()
.setUid(TARGET_UID)
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.build();
List<Anomaly> mAnomalies = mWakelockAnomalyDetector.detectAnomalies(mBatteryStatsHelper,
TARGET_PACKAGE_NAME);
assertThat(mAnomalies).containsExactly(targetAnomaly);
} }
@Test @Test

View File

@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.anomaly.checker;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyLong;
@@ -56,8 +57,10 @@ import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class WakeupAlarmAnomalyDetectorTest { public class WakeupAlarmAnomalyDetectorTest {
private static final String TARGET_PACKAGE_NAME = "com.android.app";
private static final int ANOMALY_UID = 111; private static final int ANOMALY_UID = 111;
private static final int NORMAL_UID = 222; private static final int NORMAL_UID = 222;
private static final int TARGET_UID = 333;
private static final long RUNNING_TIME_MS = 2 * DateUtils.HOUR_IN_MILLIS; private static final long RUNNING_TIME_MS = 2 * DateUtils.HOUR_IN_MILLIS;
private static final int ANOMALY_WAKEUP_COUNT = 500; private static final int ANOMALY_WAKEUP_COUNT = 500;
private static final int NORMAL_WAKEUP_COUNT = 50; private static final int NORMAL_WAKEUP_COUNT = 50;
@@ -68,10 +71,14 @@ public class WakeupAlarmAnomalyDetectorTest {
@Mock @Mock
private BatterySipper mNormalSipper; private BatterySipper mNormalSipper;
@Mock @Mock
private BatterySipper mTargetSipper;
@Mock
private BatteryStats.Uid mAnomalyUid; private BatteryStats.Uid mAnomalyUid;
@Mock @Mock
private BatteryStats.Uid mNormalUid; private BatteryStats.Uid mNormalUid;
@Mock @Mock
private BatteryStats.Uid mTargetUid;
@Mock
private BatteryUtils mBatteryUtils; private BatteryUtils mBatteryUtils;
@Mock @Mock
private ApplicationInfo mApplicationInfo; private ApplicationInfo mApplicationInfo;
@@ -101,10 +108,13 @@ public class WakeupAlarmAnomalyDetectorTest {
doReturn(ANOMALY_UID).when(mAnomalyUid).getUid(); doReturn(ANOMALY_UID).when(mAnomalyUid).getUid();
mNormalSipper.uidObj = mNormalUid; mNormalSipper.uidObj = mNormalUid;
doReturn(NORMAL_UID).when(mNormalUid).getUid(); doReturn(NORMAL_UID).when(mNormalUid).getUid();
mTargetSipper.uidObj = mTargetUid;
doReturn(TARGET_UID).when(mTargetUid).getUid();
mUsageList = new ArrayList<>(); mUsageList = new ArrayList<>();
mUsageList.add(mAnomalySipper); mUsageList.add(mAnomalySipper);
mUsageList.add(mNormalSipper); mUsageList.add(mNormalSipper);
mUsageList.add(mTargetSipper);
doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
mWakeupAlarmAnomalyDetector = spy(new WakeupAlarmAnomalyDetector(mContext, mPolicy)); mWakeupAlarmAnomalyDetector = spy(new WakeupAlarmAnomalyDetector(mContext, mPolicy));
@@ -113,18 +123,45 @@ public class WakeupAlarmAnomalyDetectorTest {
@Test @Test
public void testDetectAnomalies_containsAnomaly_detectIt() { public void testDetectAnomalies_containsAnomaly_detectIt() {
doReturn(-1).when(mBatteryUtils).getPackageUid(nullable(String.class));
doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid( doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid(
mAnomalyUid); mAnomalyUid);
doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid(
mTargetUid);
doReturn(NORMAL_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid( doReturn(NORMAL_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid(
mNormalUid); mNormalUid);
final Anomaly anomaly = new Anomaly.Builder() final Anomaly anomaly = new Anomaly.Builder()
.setUid(ANOMALY_UID) .setUid(ANOMALY_UID)
.setType(Anomaly.AnomalyType.WAKEUP_ALARM) .setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.build(); .build();
final Anomaly targetAnomaly = new Anomaly.Builder()
.setUid(TARGET_UID)
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.build();
List<Anomaly> mAnomalies = mWakeupAlarmAnomalyDetector.detectAnomalies(mBatteryStatsHelper); List<Anomaly> mAnomalies = mWakeupAlarmAnomalyDetector.detectAnomalies(mBatteryStatsHelper);
assertThat(mAnomalies).containsExactly(anomaly); assertThat(mAnomalies).containsExactly(anomaly, targetAnomaly);
}
@Test
public void testDetectAnomalies_detectTargetAnomaly_detectIt() {
doReturn(TARGET_UID).when(mBatteryUtils).getPackageUid(TARGET_PACKAGE_NAME);
doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid(
mAnomalyUid);
doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid(
mTargetUid);
doReturn(NORMAL_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector).getWakeupAlarmCountFromUid(
mNormalUid);
final Anomaly targetAnomaly = new Anomaly.Builder()
.setUid(TARGET_UID)
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.build();
List<Anomaly> mAnomalies = mWakeupAlarmAnomalyDetector.detectAnomalies(mBatteryStatsHelper,
TARGET_PACKAGE_NAME);
assertThat(mAnomalies).containsExactly(targetAnomaly);
} }
@Test @Test