Merge "Store mitigation call history before reboot"
This commit is contained in:
committed by
Android (Google) Code Review
commit
8997684906
@@ -52,9 +52,7 @@ import com.android.internal.util.XmlUtils;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -149,6 +147,7 @@ public class PackageWatchdog {
|
||||
private static final String ATTR_DURATION = "duration";
|
||||
private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration";
|
||||
private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check";
|
||||
private static final String ATTR_MITIGATION_CALLS = "mitigation-calls";
|
||||
|
||||
@GuardedBy("PackageWatchdog.class")
|
||||
private static PackageWatchdog sPackageWatchdog;
|
||||
@@ -1067,6 +1066,33 @@ public class PackageWatchdog {
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert a {@code LongArrayQueue} to a String of comma-separated values. */
|
||||
public static String longArrayQueueToString(LongArrayQueue queue) {
|
||||
if (queue.size() > 0) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(queue.get(0));
|
||||
for (int i = 1; i < queue.size(); i++) {
|
||||
sb.append(",");
|
||||
sb.append(queue.get(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/** Parse a comma-separated String of longs into a LongArrayQueue. */
|
||||
public static LongArrayQueue parseLongArrayQueue(String commaSeparatedValues) {
|
||||
LongArrayQueue result = new LongArrayQueue();
|
||||
if (!TextUtils.isEmpty(commaSeparatedValues)) {
|
||||
String[] values = commaSeparatedValues.split(",");
|
||||
for (String value : values) {
|
||||
result.addLast(Long.parseLong(value));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/** Dump status of every observer in mAllObservers. */
|
||||
public void dump(IndentingPrintWriter pw) {
|
||||
pw.println("Package Watchdog status");
|
||||
@@ -1240,16 +1266,7 @@ public class PackageWatchdog {
|
||||
while (XmlUtils.nextElementWithin(parser, innerDepth)) {
|
||||
if (TAG_PACKAGE.equals(parser.getName())) {
|
||||
try {
|
||||
String packageName = parser.getAttributeValue(
|
||||
null, ATTR_NAME);
|
||||
long duration = parser.getAttributeLong(
|
||||
null, ATTR_DURATION);
|
||||
long healthCheckDuration = parser.getAttributeLong(
|
||||
null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION);
|
||||
boolean hasPassedHealthCheck = parser.getAttributeBoolean(
|
||||
null, ATTR_PASSED_HEALTH_CHECK, false);
|
||||
MonitoredPackage pkg = watchdog.newMonitoredPackage(packageName,
|
||||
duration, healthCheckDuration, hasPassedHealthCheck);
|
||||
MonitoredPackage pkg = watchdog.parseMonitoredPackage(parser);
|
||||
if (pkg != null) {
|
||||
packages.add(pkg);
|
||||
}
|
||||
@@ -1305,16 +1322,31 @@ public class PackageWatchdog {
|
||||
|
||||
MonitoredPackage newMonitoredPackage(
|
||||
String name, long durationMs, boolean hasPassedHealthCheck) {
|
||||
return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck);
|
||||
return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck,
|
||||
new LongArrayQueue());
|
||||
}
|
||||
|
||||
MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs,
|
||||
boolean hasPassedHealthCheck) {
|
||||
boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls) {
|
||||
VersionedPackage pkg = getVersionedPackage(name);
|
||||
if (pkg == null) {
|
||||
return null;
|
||||
}
|
||||
return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs, hasPassedHealthCheck);
|
||||
return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs,
|
||||
hasPassedHealthCheck, mitigationCalls);
|
||||
}
|
||||
|
||||
MonitoredPackage parseMonitoredPackage(TypedXmlPullParser parser)
|
||||
throws XmlPullParserException {
|
||||
String packageName = parser.getAttributeValue(null, ATTR_NAME);
|
||||
long duration = parser.getAttributeLong(null, ATTR_DURATION);
|
||||
long healthCheckDuration = parser.getAttributeLong(null,
|
||||
ATTR_EXPLICIT_HEALTH_CHECK_DURATION);
|
||||
boolean hasPassedHealthCheck = parser.getAttributeBoolean(null, ATTR_PASSED_HEALTH_CHECK);
|
||||
LongArrayQueue mitigationCalls = parseLongArrayQueue(
|
||||
parser.getAttributeValue(null, ATTR_MITIGATION_CALLS));
|
||||
return newMonitoredPackage(packageName,
|
||||
duration, healthCheckDuration, hasPassedHealthCheck, mitigationCalls);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1332,7 +1364,7 @@ public class PackageWatchdog {
|
||||
// Times when an observer was called to mitigate this package's failure. Sorted in
|
||||
// ascending order.
|
||||
@GuardedBy("mLock")
|
||||
private final LongArrayQueue mMitigationCalls = new LongArrayQueue();
|
||||
private final LongArrayQueue mMitigationCalls;
|
||||
// One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after
|
||||
// methods that could change the health check state: handleElapsedTimeLocked and
|
||||
// tryPassHealthCheckLocked
|
||||
@@ -1353,12 +1385,14 @@ public class PackageWatchdog {
|
||||
@GuardedBy("mLock")
|
||||
private long mHealthCheckDurationMs = Long.MAX_VALUE;
|
||||
|
||||
private MonitoredPackage(VersionedPackage pkg, long durationMs,
|
||||
long healthCheckDurationMs, boolean hasPassedHealthCheck) {
|
||||
MonitoredPackage(VersionedPackage pkg, long durationMs,
|
||||
long healthCheckDurationMs, boolean hasPassedHealthCheck,
|
||||
LongArrayQueue mitigationCalls) {
|
||||
mPackage = pkg;
|
||||
mDurationMs = durationMs;
|
||||
mHealthCheckDurationMs = healthCheckDurationMs;
|
||||
mHasPassedHealthCheck = hasPassedHealthCheck;
|
||||
mMitigationCalls = mitigationCalls;
|
||||
updateHealthCheckStateLocked();
|
||||
}
|
||||
|
||||
@@ -1370,6 +1404,8 @@ public class PackageWatchdog {
|
||||
out.attributeLong(null, ATTR_DURATION, mDurationMs);
|
||||
out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs);
|
||||
out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck);
|
||||
LongArrayQueue normalizedCalls = normalizeMitigationCalls();
|
||||
out.attribute(null, ATTR_MITIGATION_CALLS, longArrayQueueToString(normalizedCalls));
|
||||
out.endTag(null, TAG_PACKAGE);
|
||||
}
|
||||
|
||||
@@ -1422,6 +1458,23 @@ public class PackageWatchdog {
|
||||
return mMitigationCalls.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Before writing to disk, make the mitigation call timestamps relative to the current
|
||||
* system uptime. This is because they need to be relative to the uptime which will reset
|
||||
* at the next boot.
|
||||
*
|
||||
* @return a LongArrayQueue of the mitigation calls relative to the current system uptime.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
public LongArrayQueue normalizeMitigationCalls() {
|
||||
LongArrayQueue normalized = new LongArrayQueue();
|
||||
final long now = mSystemClock.uptimeMillis();
|
||||
for (int i = 0; i < mMitigationCalls.size(); i++) {
|
||||
normalized.addLast(mMitigationCalls.get(i) - now);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial health check duration.
|
||||
*
|
||||
@@ -1582,6 +1635,16 @@ public class PackageWatchdog {
|
||||
private long toPositive(long value) {
|
||||
return value > 0 ? value : Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
/** Compares the equality of this object with another {@link MonitoredPackage}. */
|
||||
@VisibleForTesting
|
||||
boolean isEqualTo(MonitoredPackage pkg) {
|
||||
return (getName().equals(pkg.getName()))
|
||||
&& mDurationMs == pkg.mDurationMs
|
||||
&& mHasPassedHealthCheck == pkg.mHasPassedHealthCheck
|
||||
&& mHealthCheckDurationMs == pkg.mHealthCheckDurationMs
|
||||
&& (mMitigationCalls.toString()).equals(pkg.mMitigationCalls.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
@@ -43,10 +44,15 @@ import android.os.SystemProperties;
|
||||
import android.os.test.TestLooper;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.LongArrayQueue;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.TypedXmlSerializer;
|
||||
import android.util.Xml;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.ExtendedMockito;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.server.PackageWatchdog.HealthCheckState;
|
||||
import com.android.server.PackageWatchdog.MonitoredPackage;
|
||||
import com.android.server.PackageWatchdog.PackageHealthObserver;
|
||||
@@ -64,6 +70,7 @@ import org.mockito.quality.Strictness;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -739,7 +746,8 @@ public class PackageWatchdogTest {
|
||||
false /* hasPassedHealthCheck */);
|
||||
MonitoredPackage m2 = wd.newMonitoredPackage(APP_B, LONG_DURATION, false);
|
||||
MonitoredPackage m3 = wd.newMonitoredPackage(APP_C, LONG_DURATION, false);
|
||||
MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true);
|
||||
MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true,
|
||||
new LongArrayQueue());
|
||||
|
||||
// Verify transition: inactive -> active -> passed
|
||||
// Verify initially inactive
|
||||
@@ -1210,6 +1218,73 @@ public class PackageWatchdogTest {
|
||||
assertThat(observer.mMitigationCounts).isEqualTo(List.of(1, 2, 3, 3, 2, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalizingMitigationCalls() {
|
||||
PackageWatchdog watchdog = createWatchdog();
|
||||
|
||||
LongArrayQueue mitigationCalls = new LongArrayQueue();
|
||||
mitigationCalls.addLast(1000);
|
||||
mitigationCalls.addLast(2000);
|
||||
mitigationCalls.addLast(3000);
|
||||
|
||||
MonitoredPackage pkg = watchdog.newMonitoredPackage(
|
||||
"test", 123, 456, true, mitigationCalls);
|
||||
|
||||
// Make current system uptime 10000ms.
|
||||
moveTimeForwardAndDispatch(9999);
|
||||
|
||||
LongArrayQueue expectedCalls = pkg.normalizeMitigationCalls();
|
||||
|
||||
assertThat(expectedCalls.size()).isEqualTo(mitigationCalls.size());
|
||||
|
||||
for (int i = 0; i < mitigationCalls.size(); i++) {
|
||||
assertThat(expectedCalls.get(i)).isEqualTo(mitigationCalls.get(i) - 10000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a {@link MonitoredPackage} may be correctly written and read in order to persist
|
||||
* across reboots.
|
||||
*/
|
||||
@Test
|
||||
public void testWritingAndReadingMonitoredPackage() throws Exception {
|
||||
PackageWatchdog watchdog = createWatchdog();
|
||||
|
||||
LongArrayQueue mitigationCalls = new LongArrayQueue();
|
||||
mitigationCalls.addLast(1000);
|
||||
mitigationCalls.addLast(2000);
|
||||
mitigationCalls.addLast(3000);
|
||||
MonitoredPackage writePkg = watchdog.newMonitoredPackage(
|
||||
"test.package", 1000, 2000, true, mitigationCalls);
|
||||
|
||||
// Move time forward so that the current uptime is 4000ms. Therefore, the written mitigation
|
||||
// calls will each be reduced by 4000.
|
||||
moveTimeForwardAndDispatch(3999);
|
||||
LongArrayQueue expectedCalls = new LongArrayQueue();
|
||||
expectedCalls.addLast(-3000);
|
||||
expectedCalls.addLast(-2000);
|
||||
expectedCalls.addLast(-1000);
|
||||
MonitoredPackage expectedPkg = watchdog.newMonitoredPackage(
|
||||
"test.package", 1000, 2000, true, expectedCalls);
|
||||
|
||||
// Write the package
|
||||
File tmpFile = File.createTempFile("package-watchdog-test", ".xml");
|
||||
AtomicFile testFile = new AtomicFile(tmpFile);
|
||||
FileOutputStream stream = testFile.startWrite();
|
||||
TypedXmlSerializer outputSerializer = Xml.resolveSerializer(stream);
|
||||
outputSerializer.startDocument(null, true);
|
||||
writePkg.writeLocked(outputSerializer);
|
||||
outputSerializer.endDocument();
|
||||
testFile.finishWrite(stream);
|
||||
|
||||
// Read the package
|
||||
TypedXmlPullParser parser = Xml.resolvePullParser(testFile.openRead());
|
||||
XmlUtils.beginDocument(parser, "package");
|
||||
MonitoredPackage readPkg = watchdog.parseMonitoredPackage(parser);
|
||||
|
||||
assertTrue(readPkg.isEqualTo(expectedPkg));
|
||||
}
|
||||
|
||||
private void adoptShellPermissions(String... permissions) {
|
||||
InstrumentationRegistry
|
||||
.getInstrumentation()
|
||||
|
||||
Reference in New Issue
Block a user