Merge "Update wakeup anomaly to exclude blacklisted wakeups."
This commit is contained in:
committed by
Android (Google) Code Review
commit
88a2ed1954
@@ -17,6 +17,7 @@
|
||||
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;
|
||||
@@ -25,6 +26,10 @@ import android.util.Log;
|
||||
|
||||
import com.android.settings.wrapper.KeyValueListParserWrapper;
|
||||
|
||||
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}
|
||||
@@ -45,6 +50,8 @@ public class AnomalyDetectionPolicy {
|
||||
@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";
|
||||
|
||||
/**
|
||||
@@ -95,6 +102,14 @@ public class AnomalyDetectionPolicy {
|
||||
*/
|
||||
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
|
||||
*
|
||||
@@ -121,15 +136,18 @@ public class AnomalyDetectionPolicy {
|
||||
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);
|
||||
}
|
||||
@@ -150,4 +168,14 @@ public class AnomalyDetectionPolicy {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ import android.os.BatteryStats;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.os.BatterySipper;
|
||||
import com.android.internal.os.BatteryStatsHelper;
|
||||
@@ -29,10 +31,12 @@ import com.android.settings.fuelgauge.BatteryUtils;
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Check whether apps has too many wakeup alarms
|
||||
@@ -42,6 +46,7 @@ public class WakeupAlarmAnomalyDetector implements AnomalyDetector {
|
||||
@VisibleForTesting
|
||||
BatteryUtils mBatteryUtils;
|
||||
private long mWakeupAlarmThreshold;
|
||||
private Set<String> mWakeupBlacklistedTags;
|
||||
private Context mContext;
|
||||
private AnomalyUtils mAnomalyUtils;
|
||||
|
||||
@@ -56,6 +61,7 @@ public class WakeupAlarmAnomalyDetector implements AnomalyDetector {
|
||||
mBatteryUtils = BatteryUtils.getInstance(context);
|
||||
mAnomalyUtils = anomalyUtils;
|
||||
mWakeupAlarmThreshold = policy.wakeupAlarmThreshold;
|
||||
mWakeupBlacklistedTags = policy.wakeupBlacklistedTags;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,11 +129,14 @@ public class WakeupAlarmAnomalyDetector implements AnomalyDetector {
|
||||
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;
|
||||
|
@@ -56,12 +56,22 @@ public class KeyValueListParserWrapper {
|
||||
* 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.
|
||||
* @return the string value associated with the key.
|
||||
* @return the boolean value associated with the key.
|
||||
*/
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
return mParser.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public String getString(String key, String defaultValue) {
|
||||
return mParser.getString(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for key as a long.
|
||||
* @param key The key to lookup.
|
||||
|
@@ -41,11 +41,13 @@ import org.robolectric.annotation.Config;
|
||||
@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;
|
||||
@@ -59,7 +61,7 @@ public class AnomalyDetectionPolicyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInit_containsDataFromSettings() {
|
||||
public void testInit_usesConfigValues() {
|
||||
AnomalyDetectionPolicy anomalyDetectionPolicy = createAnomalyPolicyWithConfig();
|
||||
|
||||
assertThat(anomalyDetectionPolicy.anomalyDetectionEnabled).isTrue();
|
||||
@@ -67,12 +69,14 @@ public class AnomalyDetectionPolicyTest {
|
||||
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
|
||||
@@ -82,18 +86,19 @@ public class AnomalyDetectionPolicyTest {
|
||||
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(
|
||||
@@ -104,18 +109,37 @@ public class AnomalyDetectionPolicyTest {
|
||||
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);
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ import android.os.BatteryStats;
|
||||
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 org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
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 @@ public class WakeupAlarmAnomalyDetectorTest {
|
||||
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 @@ public class WakeupAlarmAnomalyDetectorTest {
|
||||
@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 @@ public class WakeupAlarmAnomalyDetectorTest {
|
||||
|
||||
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 @@ public class WakeupAlarmAnomalyDetectorTest {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user