summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvinankumar Vellore Suriyakumar <avellore@google.com>2016-01-13 18:52:59 -0800
committerAvinankumar Vellore Suriyakumar <avellore@google.com>2016-01-19 17:19:13 -0800
commitb9b98773204a467f7a6100ff62a33fbb00c9eaf3 (patch)
treed1cc6f54fc6c860cc80680a901ed4cf58a90f6d9
parenta81b9db05d513c1284aed2a2802a193f2ec88b60 (diff)
downloadloganalysis-b9b98773204a467f7a6100ff62a33fbb00c9eaf3.tar.gz
Add more information to analysis
Added more information to analysis. Also added rule for Interrupts Change-Id: I090ec531d170693a3a96047a02c3b057cfb80bb4
-rw-r--r--src/com/android/loganalysis/item/InterruptItem.java7
-rw-r--r--src/com/android/loganalysis/parser/InterruptParser.java2
-rw-r--r--src/com/android/loganalysis/rule/InterruptRule.java87
-rw-r--r--src/com/android/loganalysis/rule/LocationUsageRule.java56
-rw-r--r--src/com/android/loganalysis/rule/ProcessUsageRule.java80
-rw-r--r--src/com/android/loganalysis/rule/RuleEngine.java1
-rw-r--r--src/com/android/loganalysis/rule/WakelockRule.java28
-rw-r--r--src/com/android/loganalysis/rule/WifiStatsRule.java33
-rw-r--r--tests/src/com/android/loganalysis/rule/InterruptRuleTest.java101
-rw-r--r--tests/src/com/android/loganalysis/rule/LocationUsageRuleTest.java100
-rw-r--r--tests/src/com/android/loganalysis/rule/ProcessUsageRuleTest.java111
-rw-r--r--tests/src/com/android/loganalysis/rule/WakelockRuleTest.java108
-rw-r--r--tests/src/com/android/loganalysis/rule/WifiStatsRuleTest.java87
13 files changed, 741 insertions, 60 deletions
diff --git a/src/com/android/loganalysis/item/InterruptItem.java b/src/com/android/loganalysis/item/InterruptItem.java
index 1169004..4ff7662 100644
--- a/src/com/android/loganalysis/item/InterruptItem.java
+++ b/src/com/android/loganalysis/item/InterruptItem.java
@@ -125,6 +125,13 @@ public class InterruptItem implements IItem {
}
/**
+ * Get a list of {@link InterruptInfoItem} objects
+ */
+ public List<InterruptInfoItem> getInterrupts() {
+ return (List<InterruptInfoItem>) mInterrupts;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
diff --git a/src/com/android/loganalysis/parser/InterruptParser.java b/src/com/android/loganalysis/parser/InterruptParser.java
index 26417c4..27ccc4c 100644
--- a/src/com/android/loganalysis/parser/InterruptParser.java
+++ b/src/com/android/loganalysis/parser/InterruptParser.java
@@ -70,7 +70,7 @@ public class InterruptParser implements IParser {
}
private InterruptCategory getInterruptCategory(String interruptName) {
- if (interruptName.contains("bcmsdh_sdmmc")) {
+ if (interruptName.contains("bcmsdh_sdmmc") || interruptName.contains("msm_pcie_wake")) {
return InterruptCategory.WIFI_INTERRUPT;
} else if (interruptName.contains("smd-modem") ||
interruptName.contains("smsm-modem")) {
diff --git a/src/com/android/loganalysis/rule/InterruptRule.java b/src/com/android/loganalysis/rule/InterruptRule.java
new file mode 100644
index 0000000..ab8e678
--- /dev/null
+++ b/src/com/android/loganalysis/rule/InterruptRule.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.loganalysis.rule;
+
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.InterruptItem;
+import com.android.loganalysis.item.InterruptItem.InterruptInfoItem;
+import com.android.loganalysis.item.InterruptItem.InterruptCategory;
+
+import java.util.concurrent.TimeUnit;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Rules definition for Interrupt rule
+ */
+public class InterruptRule extends AbstractPowerRule {
+
+ private static final String INTERRUPT_ANALYSIS = "INTERRUPT_ANALYSIS";
+ private static final long INTERRUPT_THRESHOLD_MS = 120000;
+
+ private List<InterruptInfoItem> mOffendingInterruptsList;
+
+ public InterruptRule (BugreportItem bugreportItem) {
+ super(bugreportItem);
+ }
+
+ @Override
+ public void applyRule() {
+ mOffendingInterruptsList = new ArrayList<InterruptInfoItem>();
+ InterruptItem interruptItem = getDetailedAnalysisItem().getInterruptItem();
+ if (interruptItem == null || getTimeOnBattery() < 0) {
+ return;
+ }
+ for (InterruptInfoItem interrupts : interruptItem.getInterrupts()) {
+ final long interruptsPerMs = getTimeOnBattery()/interrupts.getInterruptCount();
+ if (interruptsPerMs < INTERRUPT_THRESHOLD_MS) {
+ mOffendingInterruptsList.add(interrupts);
+ }
+ }
+ }
+
+ @Override
+ public JSONObject getAnalysis() {
+ JSONObject interruptAnalysis = new JSONObject();
+ StringBuilder analysis = new StringBuilder();
+ if (mOffendingInterruptsList == null || mOffendingInterruptsList.size() <= 0) {
+ analysis.append(String.format(
+ "No interrupts woke up device more frequent than %d secs.",
+ TimeUnit.MILLISECONDS.toSeconds(INTERRUPT_THRESHOLD_MS)));
+ } else {
+ for (InterruptInfoItem interrupts : mOffendingInterruptsList) {
+ if (interrupts.getCategory() != InterruptCategory.UNKNOWN_INTERRUPT) {
+ analysis.append(String.format("Frequent interrupts from %s (%s). ",
+ interrupts.getCategory(),
+ interrupts.getName()));
+ } else {
+ analysis.append(String.format("Frequent interrupts from %s. ",
+ interrupts.getName()));
+ }
+ }
+ }
+ try {
+ interruptAnalysis.put(INTERRUPT_ANALYSIS, analysis.toString().trim());
+ } catch (JSONException e) {
+ // do nothing
+ }
+ return interruptAnalysis;
+ }
+}
diff --git a/src/com/android/loganalysis/rule/LocationUsageRule.java b/src/com/android/loganalysis/rule/LocationUsageRule.java
index 71defa2..ad208aa 100644
--- a/src/com/android/loganalysis/rule/LocationUsageRule.java
+++ b/src/com/android/loganalysis/rule/LocationUsageRule.java
@@ -21,13 +21,15 @@ import com.android.loganalysis.item.LocationDumpsItem;
import com.android.loganalysis.item.LocationDumpsItem.LocationInfoItem;
import java.util.concurrent.TimeUnit;
+import java.util.ArrayList;
+import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
/**
- * Rules definition for Process usage
+ * Rules definition for Location usage
*/
public class LocationUsageRule extends AbstractPowerRule {
@@ -36,48 +38,58 @@ public class LocationUsageRule extends AbstractPowerRule {
// GSA requests for location every 285 seconds, anything more frequent is an issue
private static final int LOCATION_INTERVAL_THRESHOLD = 285;
- private StringBuffer mAnalysisBuffer;
-
- private BugreportItem mBugreportItem = null;
+ private List<LocationInfoItem> mOffendingLocationRequestList;
+ private BugreportItem mBugreportItem;
public LocationUsageRule (BugreportItem bugreportItem) {
super(bugreportItem);
mBugreportItem = bugreportItem;
}
-
@Override
public void applyRule() {
- mAnalysisBuffer = new StringBuffer();
+ mOffendingLocationRequestList = new ArrayList<LocationInfoItem>();
+ if (mBugreportItem.getActivityService() == null || getTimeOnBattery() <= 0) {
+ return;
+ }
LocationDumpsItem locationDumpsItem = mBugreportItem.getActivityService()
.getLocationDumps();
+ if (locationDumpsItem == null) {
+ return;
+ }
final long locationRequestThresholdMs = (long) (getTimeOnBattery() *
LOCATION_REQUEST_DURATION_THRESHOLD);
- if (locationDumpsItem != null) {
- for (LocationInfoItem locationClient : locationDumpsItem.getLocationClients()) {
- final String packageName = locationClient.getPackage();
- final String priority = locationClient.getPriority();
- final int effectiveIntervalSec = locationClient.getEffectiveInterval();
- if (effectiveIntervalSec < LOCATION_INTERVAL_THRESHOLD &&
- !priority.equals("PRIORITY_NO_POWER") &&
- (TimeUnit.MINUTES.toMillis(locationClient.getDuration()) >
- locationRequestThresholdMs)) {
- mAnalysisBuffer.append(String.format("Package %s is requesting for location "
- + "updates every %d secs with priority %s", packageName,
- effectiveIntervalSec, priority));
- }
+
+ for (LocationInfoItem locationClient : locationDumpsItem.getLocationClients()) {
+ final String priority = locationClient.getPriority();
+ final int effectiveIntervalSec = locationClient.getEffectiveInterval();
+ if (effectiveIntervalSec < LOCATION_INTERVAL_THRESHOLD &&
+ !priority.equals("PRIORITY_NO_POWER") &&
+ (TimeUnit.MINUTES.toMillis(locationClient.getDuration()) >
+ locationRequestThresholdMs)) {
+ mOffendingLocationRequestList.add(locationClient);
}
}
}
@Override
public JSONObject getAnalysis() {
- JSONObject usageAnalysis = new JSONObject();
+ JSONObject locationAnalysis = new JSONObject();
+ StringBuilder analysis = new StringBuilder();
+ if (mOffendingLocationRequestList == null || mOffendingLocationRequestList.size() <= 0) {
+ analysis.append("No apps requested for frequent location updates. ");
+ } else {
+ for (LocationInfoItem locationClient : mOffendingLocationRequestList) {
+ analysis.append(String.format("Package %s is requesting for location "
+ +"updates every %d secs with priority %s.", locationClient.getPackage(),
+ locationClient.getEffectiveInterval(), locationClient.getPriority()));
+ }
+ }
try {
- usageAnalysis.put(LOCATION_USAGE_ANALYSIS, mAnalysisBuffer.toString());
+ locationAnalysis.put(LOCATION_USAGE_ANALYSIS, analysis.toString().trim());
} catch (JSONException e) {
// do nothing
}
- return usageAnalysis;
+ return locationAnalysis;
}
}
diff --git a/src/com/android/loganalysis/rule/ProcessUsageRule.java b/src/com/android/loganalysis/rule/ProcessUsageRule.java
index 4e15aee..876bdd1 100644
--- a/src/com/android/loganalysis/rule/ProcessUsageRule.java
+++ b/src/com/android/loganalysis/rule/ProcessUsageRule.java
@@ -17,9 +17,14 @@
package com.android.loganalysis.rule;
import com.android.loganalysis.item.BugreportItem;
-import com.android.loganalysis.item.DumpsysProcStatsItem;
import com.android.loganalysis.item.ProcessUsageItem;
import com.android.loganalysis.item.ProcessUsageItem.ProcessUsageInfoItem;
+import com.android.loganalysis.item.ProcessUsageItem.SensorInfoItem;
+
+import com.android.loganalysis.util.NumberFormattingUtil;
+
+import java.util.ArrayList;
+import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
@@ -30,10 +35,13 @@ import org.json.JSONObject;
*/
public class ProcessUsageRule extends AbstractPowerRule {
- private static final String PROCESS_USAGE_ANALYSIS = "PROCESS_USAGE_ANALYSIS";
+ private static final String ALARM_USAGE_ANALYSIS = "ALARM_USAGE_ANALYSIS";
+ private static final String SENSOR_USAGE_ANALYSIS = "SENSOR_USAGE_ANALYSIS";
private static final long ALARM_THRESHOLD = 60000;
+ private static final float SENSOR_ACTIVE_TIME_THRESHOLD_PERCENTAGE = 0.1f; // 10%
- private StringBuffer mAnalysisBuffer;
+ private List<ProcessUsageInfoItem> mOffendingAlarmList;
+ private List<ProcessUsageInfoItem> mOffendingSensorList;
public ProcessUsageRule (BugreportItem bugreportItem) {
super(bugreportItem);
@@ -42,34 +50,70 @@ public class ProcessUsageRule extends AbstractPowerRule {
@Override
public void applyRule() {
- mAnalysisBuffer = new StringBuffer();
+ mOffendingAlarmList = new ArrayList<ProcessUsageInfoItem>();
+ mOffendingSensorList = new ArrayList<ProcessUsageInfoItem>();
+
ProcessUsageItem processUsageItem = getDetailedAnalysisItem().getProcessUsageItem();
- DumpsysProcStatsItem procStatsItem = getProcStatsItem();
- if (processUsageItem != null && procStatsItem!= null) {
+ if (processUsageItem != null && getTimeOnBattery() > 0) {
for (ProcessUsageInfoItem usage : processUsageItem.getProcessUsage()) {
if (usage.getAlarmWakeups() > 0) {
- final long alarmsPerMs = getTimeOnBattery()/usage.getAlarmWakeups();
- if (alarmsPerMs < ALARM_THRESHOLD) {
- final String processName = procStatsItem.get(usage.getProcessUID());
- if (processName != null) {
- mAnalysisBuffer.append(processName);
- } else {
- mAnalysisBuffer.append(usage.getProcessUID());
- }
- mAnalysisBuffer.append(" has requested frequent repeating alarms");
- }
+ addAlarmAnalysis(usage);
+ }
+ if (usage.getSensorUsage() != null && usage.getSensorUsage().size() > 0) {
+ addSensorAnalysis(usage);
}
}
}
}
+ private void addAlarmAnalysis(ProcessUsageInfoItem usage) {
+ final long alarmsPerMs = getTimeOnBattery()/usage.getAlarmWakeups();
+ if (alarmsPerMs < ALARM_THRESHOLD) {
+ mOffendingAlarmList.add(usage);
+ }
+ }
+
+ private void addSensorAnalysis(ProcessUsageInfoItem usage) {
+ final long sensorUsageThresholdMs = (long) (getTimeOnBattery()
+ * SENSOR_ACTIVE_TIME_THRESHOLD_PERCENTAGE);
+ for (SensorInfoItem sensorInfo : usage.getSensorUsage()) {
+ if (sensorInfo.getUsageDurationMs() > sensorUsageThresholdMs) {
+ mOffendingSensorList.add(usage);
+ }
+ }
+ }
+
@Override
public JSONObject getAnalysis() {
JSONObject usageAnalysis = new JSONObject();
+ StringBuilder alarmAnalysis = new StringBuilder();
+ if (mOffendingAlarmList == null || mOffendingAlarmList.size() <= 0) {
+ alarmAnalysis.append("No apps requested for alarms more frequent than 60 secs.");
+ } else {
+ for (ProcessUsageInfoItem alarmInfo : mOffendingAlarmList) {
+ alarmAnalysis.append(String.format(
+ "UID %s has requested frequent repeating alarms. ",
+ alarmInfo.getProcessUID()));
+ }
+ }
+ StringBuilder sensorAnalysis = new StringBuilder();
+ if (mOffendingSensorList == null || mOffendingSensorList.size() <= 0) {
+ sensorAnalysis.append("No apps used sensors more than 10% time on battery.");
+ } else {
+ for (ProcessUsageInfoItem sensorInfo : mOffendingSensorList) {
+ for (SensorInfoItem sensors : sensorInfo.getSensorUsage()) {
+ sensorAnalysis.append(String.format("sensor %s was used for %s by UID %s. ",
+ sensors.getSensorName(),
+ NumberFormattingUtil.getDuration(sensors.getUsageDurationMs()),
+ sensorInfo.getProcessUID()));
+ }
+ }
+ }
try {
- usageAnalysis.put(PROCESS_USAGE_ANALYSIS, mAnalysisBuffer.toString());
+ usageAnalysis.put(ALARM_USAGE_ANALYSIS, alarmAnalysis.toString().trim());
+ usageAnalysis.put(SENSOR_USAGE_ANALYSIS, sensorAnalysis.toString().trim());
} catch (JSONException e) {
- // do nothing
+ // do nothing
}
return usageAnalysis;
}
diff --git a/src/com/android/loganalysis/rule/RuleEngine.java b/src/com/android/loganalysis/rule/RuleEngine.java
index 61c2081..728eeb8 100644
--- a/src/com/android/loganalysis/rule/RuleEngine.java
+++ b/src/com/android/loganalysis/rule/RuleEngine.java
@@ -68,5 +68,6 @@ public class RuleEngine {
mRulesList.add(new ProcessUsageRule(mBugreportItem));
mRulesList.add(new LocationUsageRule(mBugreportItem));
mRulesList.add(new WifiStatsRule(mBugreportItem));
+ mRulesList.add(new InterruptRule(mBugreportItem));
}
}
diff --git a/src/com/android/loganalysis/rule/WakelockRule.java b/src/com/android/loganalysis/rule/WakelockRule.java
index b2db2cd..3be02ee 100644
--- a/src/com/android/loganalysis/rule/WakelockRule.java
+++ b/src/com/android/loganalysis/rule/WakelockRule.java
@@ -21,6 +21,9 @@ import com.android.loganalysis.item.WakelockItem;
import com.android.loganalysis.item.WakelockItem.WakelockInfoItem;
import com.android.loganalysis.util.NumberFormattingUtil;
+import java.util.ArrayList;
+import java.util.List;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -32,25 +35,23 @@ public class WakelockRule extends AbstractPowerRule {
private static final String WAKELOCK_ANALYSIS = "WAKELOCK_ANALYSIS";
private static final float WAKELOCK_HELD_TIME_THRESHOLD_PERCENTAGE = 0.1f; // 10%
- private String mAnalysis = null;
+ private List<WakelockInfoItem> mOffendingWakelockList;
public WakelockRule (BugreportItem bugreportItem) {
super(bugreportItem);
}
- @SuppressWarnings("cast")
@Override
public void applyRule() {
+ mOffendingWakelockList = new ArrayList<WakelockInfoItem>();
WakelockItem wakelockItem = getDetailedAnalysisItem().getWakelockItem();
- if (wakelockItem != null) {
+ if (wakelockItem != null && getTimeOnBattery() > 0) {
long wakelockThreshold = (long)(getTimeOnBattery()
* WAKELOCK_HELD_TIME_THRESHOLD_PERCENTAGE);
for (WakelockInfoItem wakelocks : wakelockItem.getWakeLocks()) {
if (wakelocks.getHeldTime() > wakelockThreshold) {
- mAnalysis = String.format("%s %s is held for %s", wakelocks.getName(),
- wakelocks.getCategory(),
- NumberFormattingUtil.getDuration(wakelocks.getHeldTime()));
+ mOffendingWakelockList.add(wakelocks);
}
}
}
@@ -59,10 +60,19 @@ public class WakelockRule extends AbstractPowerRule {
@Override
public JSONObject getAnalysis() {
JSONObject wakelockAnalysis = new JSONObject();
- try {
- if (mAnalysis != null) {
- wakelockAnalysis.put(WAKELOCK_ANALYSIS, mAnalysis);
+
+ StringBuilder analysis = new StringBuilder();
+ if (mOffendingWakelockList == null || mOffendingWakelockList.size() <= 0) {
+ analysis.append("No wakelocks were held for more than 10% of time on battery.");
+ } else {
+ for (WakelockInfoItem wakelocks : mOffendingWakelockList) {
+ analysis.append(String.format("%s %s is held for %s. ", wakelocks.getName(),
+ wakelocks.getCategory(),
+ NumberFormattingUtil.getDuration(wakelocks.getHeldTime())));
}
+ }
+ try {
+ wakelockAnalysis.put(WAKELOCK_ANALYSIS, analysis.toString().trim());
} catch (JSONException e) {
// do nothing
}
diff --git a/src/com/android/loganalysis/rule/WifiStatsRule.java b/src/com/android/loganalysis/rule/WifiStatsRule.java
index 4a20520..f9bca39 100644
--- a/src/com/android/loganalysis/rule/WifiStatsRule.java
+++ b/src/com/android/loganalysis/rule/WifiStatsRule.java
@@ -25,7 +25,7 @@ import org.json.JSONException;
import org.json.JSONObject;
/**
- * Rules definition for Process usage
+ * Rules definition for Wifi stats
*/
public class WifiStatsRule extends AbstractPowerRule {
@@ -34,7 +34,9 @@ public class WifiStatsRule extends AbstractPowerRule {
// Wifi scans are scheduled by GSA every 285 seconds, anything more frequent is an issue
private static final long WIFI_SCAN_INTERVAL_THRESHOLD_MS = 285000;
- private StringBuffer mAnalysisBuffer;
+ private long mFrequentWifiScansIntervalSecs = 0;
+ private int mNumFrequentWifiDisconnects = 0;
+
private BugreportItem mBugreportItem = null;
public WifiStatsRule (BugreportItem bugreportItem) {
@@ -44,7 +46,6 @@ public class WifiStatsRule extends AbstractPowerRule {
@Override
public void applyRule() {
- mAnalysisBuffer = new StringBuffer();
if (mBugreportItem.getDumpsys() == null || getTimeOnBattery() <= 0) {
return;
}
@@ -57,21 +58,33 @@ public class WifiStatsRule extends AbstractPowerRule {
dumpsysWifiStatsItem.getNumWifiScans();
if (observedWifiScanIntervalMs < WIFI_SCAN_INTERVAL_THRESHOLD_MS) {
- mAnalysisBuffer.append(String.format("Wifi scans happened every %d seconds.",
- TimeUnit.MILLISECONDS.toSeconds(observedWifiScanIntervalMs)));
- }
- if (dumpsysWifiStatsItem.getNumWifiDisconnects() >= WIFI_DISCONNECT_THRESHOLD) {
- mAnalysisBuffer.append(String.format("Wifi got disconnected %d times",
- dumpsysWifiStatsItem.getNumWifiDisconnects()));
+ mFrequentWifiScansIntervalSecs = TimeUnit.MILLISECONDS.toSeconds(
+ observedWifiScanIntervalMs);
}
}
+ if (dumpsysWifiStatsItem.getNumWifiDisconnects() >= WIFI_DISCONNECT_THRESHOLD) {
+ mNumFrequentWifiDisconnects = dumpsysWifiStatsItem.getNumWifiDisconnects();
+ }
}
@Override
public JSONObject getAnalysis() {
JSONObject wifiStatsAnalysis = new JSONObject();
+ StringBuilder analysis = new StringBuilder();
+ if (mFrequentWifiScansIntervalSecs == 0) {
+ analysis.append("No apps requested for frequent wifi scans. ");
+ } else {
+ analysis.append(String.format("Wifi scans happened every %d seconds. ",
+ mFrequentWifiScansIntervalSecs));
+ }
+ if (mNumFrequentWifiDisconnects == 0) {
+ analysis.append("No frequent wifi disconnects were observed. ");
+ } else {
+ analysis.append(String.format("Wifi got disconnected %d times. ",
+ mNumFrequentWifiDisconnects));
+ }
try {
- wifiStatsAnalysis.put(WIFI_STATS, mAnalysisBuffer.toString());
+ wifiStatsAnalysis.put(WIFI_STATS, analysis.toString().trim());
} catch (JSONException e) {
// do nothing
}
diff --git a/tests/src/com/android/loganalysis/rule/InterruptRuleTest.java b/tests/src/com/android/loganalysis/rule/InterruptRuleTest.java
new file mode 100644
index 0000000..7a08db0
--- /dev/null
+++ b/tests/src/com/android/loganalysis/rule/InterruptRuleTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.loganalysis.rule;
+
+import com.android.loganalysis.item.BatteryStatsDetailedInfoItem;
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.DumpsysBatteryStatsItem;
+import com.android.loganalysis.item.DumpsysItem;
+import com.android.loganalysis.item.InterruptItem;
+import com.android.loganalysis.item.InterruptItem.InterruptCategory;
+
+import junit.framework.TestCase;
+
+import org.json.JSONObject;
+
+/**
+ * Unit tests for {@link InterruptRule}
+ */
+public class InterruptRuleTest extends TestCase {
+
+ BugreportItem mBugreport;
+ DumpsysItem mDumpsys;
+ DumpsysBatteryStatsItem mDumpsysBatteryStats;
+ BatteryStatsDetailedInfoItem mBatteryStatsDetailedInfo;
+
+ @Override
+ public void setUp() {
+ mBugreport = new BugreportItem();
+ mDumpsys = new DumpsysItem();
+ mDumpsysBatteryStats = new DumpsysBatteryStatsItem();
+ mBatteryStatsDetailedInfo = new BatteryStatsDetailedInfoItem();
+
+ mBatteryStatsDetailedInfo.setTimeOnBattery(3902004);
+ mDumpsysBatteryStats.setDetailedBatteryStatsItem(mBatteryStatsDetailedInfo);
+ mDumpsys.setBatteryInfo(mDumpsysBatteryStats);
+ mBugreport.setDumpsys(mDumpsys);
+ }
+
+ /**
+ * Test interrupt analysis
+ */
+ public void testInterruptAnalysis() throws Exception {
+ InterruptItem interrupt = new InterruptItem();
+ interrupt.addInterrupt("2:bcmsdh_sdmmc:2:qcom,smd:2:msmgio", 40,
+ InterruptCategory.WIFI_INTERRUPT);
+ interrupt.addInterrupt("2:qcom,smd-rpm:2:fc4c.qcom,spmi", 7,
+ InterruptCategory.UNKNOWN_INTERRUPT);
+
+ mBatteryStatsDetailedInfo.setInterruptItem(interrupt);
+
+ InterruptRule interruptRule = new InterruptRule(mBugreport);
+ interruptRule.applyRule();
+ JSONObject analysis = interruptRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("INTERRUPT_ANALYSIS"));
+ assertEquals(analysis.getString("INTERRUPT_ANALYSIS"),
+ "Frequent interrupts from WIFI_INTERRUPT (2:bcmsdh_sdmmc:2:qcom,smd:2:msmgio).");
+ }
+
+
+ public void testNoSignificantInterruptAnalysis() throws Exception {
+ InterruptItem interrupt = new InterruptItem();
+ interrupt.addInterrupt("2:bcmsdh_sdmmc:2:qcom,smd:2:msmgio", 5,
+ InterruptCategory.WIFI_INTERRUPT);
+ interrupt.addInterrupt("2:qcom,smd-rpm:2:fc4c.qcom,spmi", 7,
+ InterruptCategory.UNKNOWN_INTERRUPT);
+
+ mBatteryStatsDetailedInfo.setInterruptItem(interrupt);
+
+ InterruptRule interruptRule = new InterruptRule(mBugreport);
+ interruptRule.applyRule();
+ JSONObject analysis = interruptRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("INTERRUPT_ANALYSIS"));
+ assertEquals(analysis.getString("INTERRUPT_ANALYSIS"),
+ "No interrupts woke up device more frequent than 120 secs.");
+ }
+
+ public void testMissingInterruptAnalysis() throws Exception {
+ InterruptRule interruptRule = new InterruptRule(mBugreport);
+ interruptRule.applyRule();
+ JSONObject analysis = interruptRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("INTERRUPT_ANALYSIS"));
+ assertEquals(analysis.getString("INTERRUPT_ANALYSIS"),
+ "No interrupts woke up device more frequent than 120 secs.");
+ }
+}
diff --git a/tests/src/com/android/loganalysis/rule/LocationUsageRuleTest.java b/tests/src/com/android/loganalysis/rule/LocationUsageRuleTest.java
new file mode 100644
index 0000000..5dd7e9e
--- /dev/null
+++ b/tests/src/com/android/loganalysis/rule/LocationUsageRuleTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.loganalysis.rule;
+
+import com.android.loganalysis.item.ActivityServiceItem;
+import com.android.loganalysis.item.BatteryStatsDetailedInfoItem;
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.DumpsysBatteryStatsItem;
+import com.android.loganalysis.item.DumpsysItem;
+import com.android.loganalysis.item.LocationDumpsItem;
+
+import junit.framework.TestCase;
+
+import org.json.JSONObject;
+
+/**
+ * Unit tests for {@link LocationUsageRule}
+ */
+public class LocationUsageRuleTest extends TestCase {
+
+ BugreportItem mBugreport;
+ DumpsysItem mDumpsys;
+ DumpsysBatteryStatsItem mDumpsysBatteryStats;
+ BatteryStatsDetailedInfoItem mBatteryStatsDetailedInfo;
+ ActivityServiceItem mActivityService;
+
+ @Override
+ public void setUp() {
+ mBugreport = new BugreportItem();
+ mDumpsys = new DumpsysItem();
+ mDumpsysBatteryStats = new DumpsysBatteryStatsItem();
+ mBatteryStatsDetailedInfo = new BatteryStatsDetailedInfoItem();
+ mActivityService = new ActivityServiceItem();
+
+ mBatteryStatsDetailedInfo.setTimeOnBattery(3902004);
+ mDumpsysBatteryStats.setDetailedBatteryStatsItem(mBatteryStatsDetailedInfo);
+ mDumpsys.setBatteryInfo(mDumpsysBatteryStats);
+ mBugreport.setDumpsys(mDumpsys);
+ mBugreport.setActivityService(mActivityService);
+ }
+
+ /**
+ * Test location usage analysis
+ */
+ public void testLocationUsageAnalysis() throws Exception {
+ LocationDumpsItem location = new LocationDumpsItem();
+ location.addLocationClient("com.google.android.gms", 1, 0, 0, "PRIORITY_NO_POWER", 140);
+ location.addLocationClient("com.google.android.gms", 5, 5, 5,
+ "PRIORITY_BALANCED_POWER_ACCURACY", 140);
+ mActivityService.setLocationDumps(location);
+
+ LocationUsageRule locationUsageRule = new LocationUsageRule(mBugreport);
+ locationUsageRule.applyRule();
+ JSONObject analysis = locationUsageRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("LOCATION_USAGE_ANALYSIS"));
+ assertEquals(analysis.getString("LOCATION_USAGE_ANALYSIS"),
+ "Package com.google.android.gms is requesting for location updates every 5 secs "
+ + "with priority PRIORITY_BALANCED_POWER_ACCURACY.");
+ }
+
+ public void testNoSignificantLocationUsageAnalysis() throws Exception {
+ LocationDumpsItem location = new LocationDumpsItem();
+ location.addLocationClient("com.google.android.gms", 1, 0, 0, "PRIORITY_NO_POWER", 140);
+ location.addLocationClient("com.google.android.gms", 285, 285, 285,
+ "PRIORITY_BALANCED_POWER_ACCURACY", 140);
+ mActivityService.setLocationDumps(location);
+
+ LocationUsageRule locationUsageRule = new LocationUsageRule(mBugreport);
+ locationUsageRule.applyRule();
+ JSONObject analysis = locationUsageRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("LOCATION_USAGE_ANALYSIS"));
+ assertEquals(analysis.getString("LOCATION_USAGE_ANALYSIS"),
+ "No apps requested for frequent location updates.");
+ }
+
+ public void testNoLocationUsageAnalysis() throws Exception {
+ LocationUsageRule locationUsageRule = new LocationUsageRule(mBugreport);
+ locationUsageRule.applyRule();
+ JSONObject analysis = locationUsageRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("LOCATION_USAGE_ANALYSIS"));
+ assertEquals(analysis.getString("LOCATION_USAGE_ANALYSIS"),
+ "No apps requested for frequent location updates.");
+ }
+}
diff --git a/tests/src/com/android/loganalysis/rule/ProcessUsageRuleTest.java b/tests/src/com/android/loganalysis/rule/ProcessUsageRuleTest.java
new file mode 100644
index 0000000..cc322dc
--- /dev/null
+++ b/tests/src/com/android/loganalysis/rule/ProcessUsageRuleTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.loganalysis.rule;
+
+import com.android.loganalysis.item.BatteryStatsDetailedInfoItem;
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.DumpsysBatteryStatsItem;
+import com.android.loganalysis.item.DumpsysItem;
+import com.android.loganalysis.item.ProcessUsageItem;
+import com.android.loganalysis.item.ProcessUsageItem.SensorInfoItem;
+
+import java.util.LinkedList;
+import junit.framework.TestCase;
+
+import org.json.JSONObject;
+
+/**
+ * Unit tests for {@link ProcessUsageRule}
+ */
+public class ProcessUsageRuleTest extends TestCase {
+
+ BugreportItem mBugreport;
+ DumpsysItem mDumpsys;
+ DumpsysBatteryStatsItem mDumpsysBatteryStats;
+ BatteryStatsDetailedInfoItem mBatteryStatsDetailedInfo;
+
+ @Override
+ public void setUp() {
+ mBugreport = new BugreportItem();
+ mDumpsys = new DumpsysItem();
+ mDumpsysBatteryStats = new DumpsysBatteryStatsItem();
+ mBatteryStatsDetailedInfo = new BatteryStatsDetailedInfoItem();
+
+ mBatteryStatsDetailedInfo.setTimeOnBattery(3902004);
+ mDumpsysBatteryStats.setDetailedBatteryStatsItem(mBatteryStatsDetailedInfo);
+ mDumpsys.setBatteryInfo(mDumpsysBatteryStats);
+ mBugreport.setDumpsys(mDumpsys);
+ }
+
+ /**
+ * Test alarm usage analysis
+ */
+ public void testAlarmAnalysis() throws Exception {
+ ProcessUsageItem processUsage = new ProcessUsageItem();
+ LinkedList<SensorInfoItem> uid0Sensor = new LinkedList<SensorInfoItem>();
+ uid0Sensor.add(new SensorInfoItem("0", 9908));
+ uid0Sensor.add(new SensorInfoItem("1", 9997));
+
+ LinkedList<SensorInfoItem> uidU0a9Sensor = new LinkedList<SensorInfoItem>();
+ uidU0a9Sensor.add(new SensorInfoItem("2", 1315));
+
+ processUsage.addProcessUsage("0", 0, uid0Sensor);
+ processUsage.addProcessUsage("u0a9", 180, uidU0a9Sensor);
+ processUsage.addProcessUsage("u0a8", 0, null);
+
+ mBatteryStatsDetailedInfo.setProcessUsageItem(processUsage);
+ ProcessUsageRule usage = new ProcessUsageRule(mBugreport);
+ usage.applyRule();
+ JSONObject analysis = usage.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("ALARM_USAGE_ANALYSIS"));
+ assertEquals(analysis.getString("ALARM_USAGE_ANALYSIS"),
+ "UID u0a9 has requested frequent repeating alarms.");
+ assertTrue(analysis.has("SENSOR_USAGE_ANALYSIS"));
+ assertEquals(analysis.getString("SENSOR_USAGE_ANALYSIS"),
+ "No apps used sensors more than 10% time on battery.");
+ }
+
+ /**
+ * Test sensor usage analysis
+ */
+ public void testSensorAnalysis() throws Exception {
+ ProcessUsageItem processUsage = new ProcessUsageItem();
+ LinkedList<SensorInfoItem> uid0Sensor = new LinkedList<SensorInfoItem>();
+ uid0Sensor.add(new SensorInfoItem("0", 9908));
+ uid0Sensor.add(new SensorInfoItem("1", 9997));
+
+ LinkedList<SensorInfoItem> uidU0a9Sensor = new LinkedList<SensorInfoItem>();
+ uidU0a9Sensor.add(new SensorInfoItem("2", 913015));
+
+ processUsage.addProcessUsage("0", 0, uid0Sensor);
+ processUsage.addProcessUsage("u0a9", 15, uidU0a9Sensor);
+ processUsage.addProcessUsage("u0a8", 0, null);
+
+ mBatteryStatsDetailedInfo.setProcessUsageItem(processUsage);
+ ProcessUsageRule usage = new ProcessUsageRule(mBugreport);
+ usage.applyRule();
+ JSONObject analysis = usage.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("SENSOR_USAGE_ANALYSIS"));
+ assertEquals(analysis.getString("SENSOR_USAGE_ANALYSIS"),
+ "sensor 2 was used for 0d 0h 15m 13s by UID u0a9.");
+
+ assertTrue(analysis.has("ALARM_USAGE_ANALYSIS"));
+ assertEquals(analysis.getString("ALARM_USAGE_ANALYSIS"),
+ "No apps requested for alarms more frequent than 60 secs.");
+ }
+}
diff --git a/tests/src/com/android/loganalysis/rule/WakelockRuleTest.java b/tests/src/com/android/loganalysis/rule/WakelockRuleTest.java
new file mode 100644
index 0000000..6900c07
--- /dev/null
+++ b/tests/src/com/android/loganalysis/rule/WakelockRuleTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.loganalysis.rule;
+
+import com.android.loganalysis.item.BatteryStatsDetailedInfoItem;
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.DumpsysBatteryStatsItem;
+import com.android.loganalysis.item.DumpsysItem;
+import com.android.loganalysis.item.WakelockItem;
+import com.android.loganalysis.item.WakelockItem.WakeLockCategory;
+
+import junit.framework.TestCase;
+
+import org.json.JSONObject;
+
+/**
+ * Unit tests for {@link WakelockRule}
+ */
+public class WakelockRuleTest extends TestCase {
+
+ BugreportItem mBugreport;
+ DumpsysItem mDumpsys;
+ DumpsysBatteryStatsItem mDumpsysBatteryStats;
+ BatteryStatsDetailedInfoItem mBatteryStatsDetailedInfo;
+
+ @Override
+ public void setUp() {
+ mBugreport = new BugreportItem();
+ mDumpsys = new DumpsysItem();
+ mDumpsysBatteryStats = new DumpsysBatteryStatsItem();
+ mBatteryStatsDetailedInfo = new BatteryStatsDetailedInfoItem();
+
+ mBatteryStatsDetailedInfo.setTimeOnBattery(3902004);
+ mDumpsysBatteryStats.setDetailedBatteryStatsItem(mBatteryStatsDetailedInfo);
+ mDumpsys.setBatteryInfo(mDumpsysBatteryStats);
+ mBugreport.setDumpsys(mDumpsys);
+ }
+
+ /**
+ * Test wakelock analysis
+ */
+ public void testWakelockAnalysis() throws Exception {
+ WakelockItem wakelock = new WakelockItem();
+ wakelock.addWakeLock("PowerManagerService.WakeLocks", 310006, 2,
+ WakeLockCategory.KERNEL_WAKELOCK);
+ wakelock.addWakeLock("msm_serial_hs_rx", 133612, 258,
+ WakeLockCategory.KERNEL_WAKELOCK);
+
+ wakelock.addWakeLock("ProxyController", "1001", 3887565, 4,
+ WakeLockCategory.PARTIAL_WAKELOCK);
+ wakelock.addWakeLock("AudioMix", "1013", 1979, 3,
+ WakeLockCategory.PARTIAL_WAKELOCK);
+
+ mBatteryStatsDetailedInfo.setWakelockItem(wakelock);
+ WakelockRule wakelockRule = new WakelockRule(mBugreport);
+ wakelockRule.applyRule();
+ JSONObject analysis = wakelockRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("WAKELOCK_ANALYSIS"));
+ assertEquals(analysis.getString("WAKELOCK_ANALYSIS"),
+ "ProxyController PARTIAL_WAKELOCK is held for 0d 1h 4m 47s.");
+ }
+
+ public void testNoSignificantWakelockAnalysis() throws Exception {
+ WakelockItem wakelock = new WakelockItem();
+ wakelock.addWakeLock("PowerManagerService.WakeLocks", 310006, 2,
+ WakeLockCategory.KERNEL_WAKELOCK);
+ wakelock.addWakeLock("msm_serial_hs_rx", 133612, 258,
+ WakeLockCategory.KERNEL_WAKELOCK);
+
+ wakelock.addWakeLock("ProxyController", "1001", 287565, 4,
+ WakeLockCategory.PARTIAL_WAKELOCK);
+ wakelock.addWakeLock("AudioMix", "1013", 1979, 3,
+ WakeLockCategory.PARTIAL_WAKELOCK);
+
+ mBatteryStatsDetailedInfo.setWakelockItem(wakelock);
+ WakelockRule wakelockRule = new WakelockRule(mBugreport);
+ wakelockRule.applyRule();
+ JSONObject analysis = wakelockRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("WAKELOCK_ANALYSIS"));
+ assertEquals(analysis.getString("WAKELOCK_ANALYSIS"),
+ "No wakelocks were held for more than 10% of time on battery.");
+ }
+
+ public void testNoWakelockAnalysis() throws Exception {
+ WakelockRule wakelockRule = new WakelockRule(mBugreport);
+ wakelockRule.applyRule();
+ JSONObject analysis = wakelockRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("WAKELOCK_ANALYSIS"));
+ assertEquals(analysis.getString("WAKELOCK_ANALYSIS"),
+ "No wakelocks were held for more than 10% of time on battery.");
+ }
+}
diff --git a/tests/src/com/android/loganalysis/rule/WifiStatsRuleTest.java b/tests/src/com/android/loganalysis/rule/WifiStatsRuleTest.java
new file mode 100644
index 0000000..628039e
--- /dev/null
+++ b/tests/src/com/android/loganalysis/rule/WifiStatsRuleTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.loganalysis.rule;
+
+import com.android.loganalysis.item.BatteryStatsDetailedInfoItem;
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.DumpsysBatteryStatsItem;
+import com.android.loganalysis.item.DumpsysItem;
+import com.android.loganalysis.item.DumpsysWifiStatsItem;
+import com.android.loganalysis.parser.BugreportParser;
+
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.json.JSONObject;
+
+/**
+ */
+public class WifiStatsRuleTest extends TestCase {
+
+ BugreportItem mBugreport;
+ DumpsysItem mDumpsys;
+ DumpsysBatteryStatsItem mDumpsysBatteryStats;
+ BatteryStatsDetailedInfoItem mBatteryStatsDetailedInfo;
+
+ @Override
+ public void setUp() {
+ mBugreport = new BugreportItem();
+ mDumpsys = new DumpsysItem();
+ mDumpsysBatteryStats = new DumpsysBatteryStatsItem();
+ mBatteryStatsDetailedInfo = new BatteryStatsDetailedInfoItem();
+
+ mBatteryStatsDetailedInfo.setTimeOnBattery(302004);
+ mDumpsysBatteryStats.setDetailedBatteryStatsItem(mBatteryStatsDetailedInfo);
+ mDumpsys.setBatteryInfo(mDumpsysBatteryStats);
+ mBugreport.setDumpsys(mDumpsys);
+ }
+
+ /**
+ * Test wifistats analysis
+ */
+ public void testWifiDisconnectAnalysis() throws Exception {
+ DumpsysWifiStatsItem wifiStats = new DumpsysWifiStatsItem();
+ wifiStats.setNumWifiDisconnect(1);
+ wifiStats.setNumWifiScan(0);
+
+ mDumpsys.setWifiStats(wifiStats);
+ WifiStatsRule wifiStatsRule = new WifiStatsRule(mBugreport);
+ wifiStatsRule.applyRule();
+ JSONObject analysis = wifiStatsRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("WIFI_STATS"));
+ assertEquals(analysis.getString("WIFI_STATS"),
+ "No apps requested for frequent wifi scans. Wifi got disconnected 1 times.");
+ }
+
+ public void testWifiScanAnalysis() throws Exception {
+ DumpsysWifiStatsItem wifiStats = new DumpsysWifiStatsItem();
+ wifiStats.setNumWifiDisconnect(0);
+ wifiStats.setNumWifiScan(3);
+
+ mDumpsys.setWifiStats(wifiStats);
+ WifiStatsRule wifiStatsRule = new WifiStatsRule(mBugreport);
+ wifiStatsRule.applyRule();
+ JSONObject analysis = wifiStatsRule.getAnalysis();
+ assertNotNull(analysis);
+ assertTrue(analysis.has("WIFI_STATS"));
+ assertEquals(analysis.getString("WIFI_STATS"),
+ "Wifi scans happened every 100 seconds. No frequent wifi disconnects were observed."
+ );
+ }
+}