Update wakeup anomaly to exclude blacklisted wakeups.
Also disables all anomalies out of the box.
Provides the ability to ignore certain wakeups if blacklisted in
AnomalyDetectionPolicy. Compares each wakeup to the blacklist and
if it exists, does not include it in the count used to compare against
the threshold.
Change-Id: I038ca966aed5a030853b4786bc201397bf4f9b70
Merged-In: I4ef548bd0952be5f0d4e36df5698f287839d0704
Fixes: 67000019
Test: robotests
diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicy.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicy.java
index 0c401b5..dd8ebc7 100644
--- a/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicy.java
+++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicy.java
@@ -17,12 +17,17 @@
package com.android.settings.fuelgauge.anomaly;
import android.content.Context;
+import android.net.Uri;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.KeyValueListParser;
import android.util.Log;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
/**
* Class to store the policy for anomaly detection, which comes from
* {@link android.provider.Settings.Global}
@@ -43,6 +48,8 @@
@VisibleForTesting
static final String KEY_WAKEUP_ALARM_THRESHOLD = "wakeup_alarm_threshold";
@VisibleForTesting
+ static final String KEY_WAKEUP_BLACKLISTED_TAGS = "wakeup_blacklisted_tags";
+ @VisibleForTesting
static final String KEY_BLUETOOTH_SCAN_THRESHOLD = "bluetooth_scan_threshold";
/**
@@ -94,6 +101,14 @@
public final long wakeupAlarmThreshold;
/**
+ * Array of blacklisted wakeups, by tag.
+ *
+ * @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
+ * @see #KEY_WAKEUP_BLACKLISTED_TAGS
+ */
+ public final Set<String> wakeupBlacklistedTags;
+
+ /**
* Threshold for bluetooth unoptimized scanning time in milli seconds
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
@@ -119,15 +134,18 @@
Log.e(TAG, "Bad anomaly detection constants");
}
- anomalyDetectionEnabled = mParserWrapper.getBoolean(KEY_ANOMALY_DETECTION_ENABLED, true);
- wakeLockDetectionEnabled = mParserWrapper.getBoolean(KEY_WAKELOCK_DETECTION_ENABLED, true);
- wakeupAlarmDetectionEnabled = mParserWrapper.getBoolean(KEY_WAKEUP_ALARM_DETECTION_ENABLED,
- false);
+ anomalyDetectionEnabled =
+ mParserWrapper.getBoolean(KEY_ANOMALY_DETECTION_ENABLED, false);
+ wakeLockDetectionEnabled =
+ mParserWrapper.getBoolean(KEY_WAKELOCK_DETECTION_ENABLED,false);
+ wakeupAlarmDetectionEnabled =
+ mParserWrapper.getBoolean(KEY_WAKEUP_ALARM_DETECTION_ENABLED,false);
bluetoothScanDetectionEnabled = mParserWrapper.getBoolean(
- KEY_BLUETOOTH_SCAN_DETECTION_ENABLED, true);
+ KEY_BLUETOOTH_SCAN_DETECTION_ENABLED, false);
wakeLockThreshold = mParserWrapper.getLong(KEY_WAKELOCK_THRESHOLD,
DateUtils.HOUR_IN_MILLIS);
- wakeupAlarmThreshold = mParserWrapper.getLong(KEY_WAKEUP_ALARM_THRESHOLD, 60);
+ wakeupAlarmThreshold = mParserWrapper.getLong(KEY_WAKEUP_ALARM_THRESHOLD, 10);
+ wakeupBlacklistedTags = parseStringSet(KEY_WAKEUP_BLACKLISTED_TAGS, null);
bluetoothScanThreshold = mParserWrapper.getLong(KEY_BLUETOOTH_SCAN_THRESHOLD,
30 * DateUtils.MINUTE_IN_MILLIS);
}
@@ -148,4 +166,14 @@
return false; // Disabled when no this type
}
}
+
+ private Set<String> parseStringSet(final String key, final Set<String> defaultSet) {
+ final String value = mParserWrapper.getString(key, null);
+ if (value != null) {
+ return Arrays.stream(value.split(":"))
+ .map(String::trim).map(Uri::decode).collect(Collectors.toSet());
+ } else {
+ return defaultSet;
+ }
+ }
}
diff --git a/src/com/android/settings/fuelgauge/anomaly/KeyValueListParserWrapper.java b/src/com/android/settings/fuelgauge/anomaly/KeyValueListParserWrapper.java
index 4a9c2a9..7d216c5 100644
--- a/src/com/android/settings/fuelgauge/anomaly/KeyValueListParserWrapper.java
+++ b/src/com/android/settings/fuelgauge/anomaly/KeyValueListParserWrapper.java
@@ -44,6 +44,14 @@
void setString(String str) throws IllegalArgumentException;
/**
+ * Get the value for key as a string.
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found.
+ * @return the string value associated with the key.
+ */
+ String getString(String key, String defaultValue);
+
+ /**
* Get the value for key as a boolean.
* @param key The key to lookup.
* @param defaultValue The value to return if the key was not found.
diff --git a/src/com/android/settings/fuelgauge/anomaly/KeyValueListParserWrapperImpl.java b/src/com/android/settings/fuelgauge/anomaly/KeyValueListParserWrapperImpl.java
index f724034..cf3aa95 100644
--- a/src/com/android/settings/fuelgauge/anomaly/KeyValueListParserWrapperImpl.java
+++ b/src/com/android/settings/fuelgauge/anomaly/KeyValueListParserWrapperImpl.java
@@ -39,6 +39,11 @@
}
@Override
+ public String getString(String key, String defaultValue) {
+ return mParser.getString(key, defaultValue);
+ }
+
+ @Override
public boolean getBoolean(String key, boolean defaultValue) {
return mParser.getBoolean(key, defaultValue);
}
diff --git a/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java b/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java
index 8823a17..936fe30 100644
--- a/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java
+++ b/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java
@@ -29,10 +29,11 @@
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
-import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Check whether apps has too many wakeup alarms
@@ -42,6 +43,7 @@
@VisibleForTesting
BatteryUtils mBatteryUtils;
private long mWakeupAlarmThreshold;
+ private Set<String> mWakeupBlacklistedTags;
private Context mContext;
private AnomalyUtils mAnomalyUtils;
@@ -56,6 +58,7 @@
mBatteryUtils = BatteryUtils.getInstance(context);
mAnomalyUtils = anomalyUtils;
mWakeupAlarmThreshold = policy.wakeupAlarmThreshold;
+ mWakeupBlacklistedTags = policy.wakeupBlacklistedTags;
}
@Override
@@ -123,11 +126,14 @@
final BatteryStats.Uid.Pkg ps = packageStats.valueAt(ipkg);
final ArrayMap<String, ? extends BatteryStats.Counter> alarms =
ps.getWakeupAlarmStats();
- for (int iwa = alarms.size() - 1; iwa >= 0; iwa--) {
- int count = alarms.valueAt(iwa).getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+ for (Map.Entry<String, ? extends BatteryStats.Counter> alarm : alarms.entrySet()) {
+ if (mWakeupBlacklistedTags != null
+ && mWakeupBlacklistedTags.contains(alarm.getKey())) {
+ continue;
+ }
+ int count = alarm.getValue().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
wakeups += count;
}
-
}
return wakeups;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicyTest.java
index 914cc2f..8d07629 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicyTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicyTest.java
@@ -40,11 +40,13 @@
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class AnomalyDetectionPolicyTest {
- private static final String ANOMALY_DETECTION_CONSTANTS_VALUE = "anomaly_detection_enabled=true"
+ private static final String ANOMALY_DETECTION_CONSTANTS_VALUE =
+ "anomaly_detection_enabled=true"
+ ",wakelock_enabled=false"
+ ",wakelock_threshold=3000"
+ ",wakeup_alarm_enabled=true"
+ ",wakeup_alarm_threshold=100"
+ + ",wakeup_blacklisted_tags=tag1:tag2:with%2Ccomma:with%3Acolon"
+ ",bluetooth_scan_enabled=true"
+ ",bluetooth_scan_threshold=2000";
private Context mContext;
@@ -58,7 +60,7 @@
}
@Test
- public void testInit_containsDataFromSettings() {
+ public void testInit_usesConfigValues() {
AnomalyDetectionPolicy anomalyDetectionPolicy = createAnomalyPolicyWithConfig();
assertThat(anomalyDetectionPolicy.anomalyDetectionEnabled).isTrue();
@@ -66,12 +68,14 @@
assertThat(anomalyDetectionPolicy.wakeLockThreshold).isEqualTo(3000);
assertThat(anomalyDetectionPolicy.wakeupAlarmDetectionEnabled).isTrue();
assertThat(anomalyDetectionPolicy.wakeupAlarmThreshold).isEqualTo(100);
+ assertThat(anomalyDetectionPolicy.wakeupBlacklistedTags)
+ .containsExactly("tag1", "tag2", "with,comma", "with:colon");
assertThat(anomalyDetectionPolicy.bluetoothScanDetectionEnabled).isTrue();
assertThat(anomalyDetectionPolicy.bluetoothScanThreshold).isEqualTo(2000);
}
@Test
- public void testInit_containsDefaultData() {
+ public void testInit_defaultValues() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.ANOMALY_DETECTION_CONSTANTS, "");
// Mock it to avoid noSuchMethodError
@@ -81,18 +85,19 @@
AnomalyDetectionPolicy anomalyDetectionPolicy = new AnomalyDetectionPolicy(mContext,
mKeyValueListParserWrapper);
- assertThat(anomalyDetectionPolicy.anomalyDetectionEnabled).isTrue();
- assertThat(anomalyDetectionPolicy.wakeLockDetectionEnabled).isTrue();
+ assertThat(anomalyDetectionPolicy.anomalyDetectionEnabled).isFalse();
+ assertThat(anomalyDetectionPolicy.wakeLockDetectionEnabled).isFalse();
assertThat(anomalyDetectionPolicy.wakeLockThreshold).isEqualTo(DateUtils.HOUR_IN_MILLIS);
assertThat(anomalyDetectionPolicy.wakeupAlarmDetectionEnabled).isFalse();
- assertThat(anomalyDetectionPolicy.wakeupAlarmThreshold).isEqualTo(60);
- assertThat(anomalyDetectionPolicy.bluetoothScanDetectionEnabled).isTrue();
+ assertThat(anomalyDetectionPolicy.wakeupAlarmThreshold).isEqualTo(10);
+ assertThat(anomalyDetectionPolicy.wakeupBlacklistedTags).isNull();
+ assertThat(anomalyDetectionPolicy.bluetoothScanDetectionEnabled).isFalse();
assertThat(anomalyDetectionPolicy.bluetoothScanThreshold).isEqualTo(
30 * DateUtils.MINUTE_IN_MILLIS);
}
@Test
- public void testIsAnomalyDetectorEnabled() {
+ public void testIsAnomalyDetectorEnabled_usesConfigValues() {
AnomalyDetectionPolicy anomalyDetectionPolicy = createAnomalyPolicyWithConfig();
assertThat(anomalyDetectionPolicy.isAnomalyDetectorEnabled(
@@ -103,18 +108,37 @@
Anomaly.AnomalyType.BLUETOOTH_SCAN)).isTrue();
}
+ @Test
+ public void testIsAnomalyDetectorEnabled_usesDefaultValues() {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ANOMALY_DETECTION_CONSTANTS, "");
+ // Mock it to avoid noSuchMethodError
+ doReturn(true).when(mKeyValueListParserWrapper).getBoolean(anyString(), eq(true));
+ doReturn(false).when(mKeyValueListParserWrapper).getBoolean(anyString(), eq(false));
+
+ AnomalyDetectionPolicy anomalyDetectionPolicy = new AnomalyDetectionPolicy(mContext,
+ mKeyValueListParserWrapper);
+
+ assertThat(anomalyDetectionPolicy.isAnomalyDetectorEnabled(
+ Anomaly.AnomalyType.WAKE_LOCK)).isFalse();
+ assertThat(anomalyDetectionPolicy.isAnomalyDetectorEnabled(
+ Anomaly.AnomalyType.WAKEUP_ALARM)).isFalse();
+ assertThat(anomalyDetectionPolicy.isAnomalyDetectorEnabled(
+ Anomaly.AnomalyType.BLUETOOTH_SCAN)).isFalse();
+ }
+
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);
+ AnomalyDetectionPolicy.KEY_ANOMALY_DETECTION_ENABLED, false);
doReturn(false).when(mKeyValueListParserWrapper).getBoolean(
- AnomalyDetectionPolicy.KEY_WAKELOCK_DETECTION_ENABLED, true);
+ AnomalyDetectionPolicy.KEY_WAKELOCK_DETECTION_ENABLED, false);
doReturn(true).when(mKeyValueListParserWrapper).getBoolean(
AnomalyDetectionPolicy.KEY_WAKEUP_ALARM_DETECTION_ENABLED, false);
doReturn(true).when(mKeyValueListParserWrapper).getBoolean(
- AnomalyDetectionPolicy.KEY_BLUETOOTH_SCAN_DETECTION_ENABLED, true);
+ AnomalyDetectionPolicy.KEY_BLUETOOTH_SCAN_DETECTION_ENABLED, false);
return new AnomalyDetectionPolicy(mContext, mKeyValueListParserWrapper);
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetectorTest.java
index 55be734..13a5ab8 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetectorTest.java
@@ -30,6 +30,7 @@
import android.os.Build;
import android.text.format.DateUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
@@ -52,6 +53,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -69,6 +71,7 @@
1 * DateUtils.HOUR_IN_MILLIS + 10 * DateUtils.MINUTE_IN_MILLIS;
private static final int ANOMALY_WAKEUP_COUNT = 500;
private static final int NORMAL_WAKEUP_COUNT = 61;
+ private static final int BLACKLISTED_WAKEUP_COUNT = 37;
private static final int ANOMALY_WAKEUP_FREQUENCY = 428; // count per hour
@Mock
private BatteryStatsHelper mBatteryStatsHelper;
@@ -87,12 +90,12 @@
@Mock
private BatteryUtils mBatteryUtils;
@Mock
- private ApplicationInfo mApplicationInfo;
- @Mock
private BatteryStats.Uid.Pkg mPkg;
@Mock
private BatteryStats.Counter mCounter;
@Mock
+ private BatteryStats.Counter mCounter2;
+ @Mock
private AnomalyDetectionPolicy mPolicy;
@Mock
private AnomalyAction mAnomalyAction;
@@ -111,6 +114,9 @@
mContext = spy(RuntimeEnvironment.application);
ReflectionHelpers.setField(mPolicy, "wakeupAlarmThreshold", 60);
+ final Set<String> blacklistedTags = new ArraySet<>();
+ blacklistedTags.add("blacklistedTag");
+ ReflectionHelpers.setField(mPolicy, "wakeupBlacklistedTags", blacklistedTags);
doReturn(false).when(mBatteryUtils).shouldHideSipper(any());
doReturn(RUNNING_TIME_MS).when(mBatteryUtils).calculateRunningTimeBasedOnStatsType(any(),
@@ -207,4 +213,20 @@
assertThat(mWakeupAlarmAnomalyDetector.getWakeupAlarmCountFromUid(mAnomalyUid)).isEqualTo(
2 * NORMAL_WAKEUP_COUNT);
}
+
+ @Test
+ public void testGetWakeupAlarmCountFromUid_filterOutBlacklistedTags() {
+ final ArrayMap<String, BatteryStats.Uid.Pkg> packageStats = new ArrayMap<>();
+ final ArrayMap<String, BatteryStats.Counter> alarms = new ArrayMap<>();
+ doReturn(alarms).when(mPkg).getWakeupAlarmStats();
+ doReturn(NORMAL_WAKEUP_COUNT).when(mCounter).getCountLocked(anyInt());
+ doReturn(BLACKLISTED_WAKEUP_COUNT).when(mCounter2).getCountLocked(anyInt());
+ doReturn(packageStats).when(mAnomalyUid).getPackageStats();
+ packageStats.put("", mPkg);
+ alarms.put("allowedTag", mCounter);
+ alarms.put("blacklistedTag", mCounter2);
+
+ assertThat(mWakeupAlarmAnomalyDetector.getWakeupAlarmCountFromUid(mAnomalyUid)).isEqualTo(
+ NORMAL_WAKEUP_COUNT);
+ }
}